diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index d482e09..b89206c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,24 +1,21 @@ ---- +______________________________________________________________________ -name: Bug Report -about: Create a bug report to help us improve pytask-stata -title: "BUG:" -labels: "bug" +name: Bug Report about: Create a bug report to help us improve pytask-stata title: +"BUG:" labels: "bug" ---- +______________________________________________________________________ - [ ] I have checked that this issue has not already been reported. - [ ] I have confirmed this bug exists on the latest version of pytask-stata. -- [ ] (optional) I have confirmed this bug exists on the `main` branch of - pytask-stata. +- [ ] (optional) I have confirmed this bug exists on the `main` branch of pytask-stata. ---- +______________________________________________________________________ -**Note**: Please read [this -guide](https://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports) detailing -how to provide the necessary information for us to reproduce your bug. +**Note**: Please read +[this guide](https://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports) +detailing how to provide the necessary information for us to reproduce your bug. #### Code Sample, a copy-pastable example diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md index 90389c7..eba1e97 100644 --- a/.github/ISSUE_TEMPLATE/documentation.md +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -1,11 +1,9 @@ ---- +______________________________________________________________________ -name: Documentation Improvement -about: Report wrong or missing documentation -title: "DOC:" -labels: "documentation" +name: Documentation Improvement about: Report wrong or missing documentation title: +"DOC:" labels: "documentation" ---- +______________________________________________________________________ #### Location of the documentation diff --git a/.github/ISSUE_TEMPLATE/enhancement.md b/.github/ISSUE_TEMPLATE/enhancement.md index 5296f8d..5402874 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.md +++ b/.github/ISSUE_TEMPLATE/enhancement.md @@ -1,16 +1,14 @@ ---- +______________________________________________________________________ -name: Enhancement -about: Suggest an idea for pytask-stata -title: "ENH:" -labels: "enhancement" +name: Enhancement about: Suggest an idea for pytask-stata title: "ENH:" labels: +"enhancement" ---- +______________________________________________________________________ #### Is your feature request related to a problem? -Provide a description of what the problem is, e.g. "I wish I could use pytask-stata -to do [...]". +Provide a description of what the problem is, e.g. "I wish I could use pytask-stata to +do \[...\]". #### Describe the solution you'd like diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 46dfc30..bad0800 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -1,17 +1,15 @@ ---- +______________________________________________________________________ -name: Submit Question -about: Ask a general question about pytask-stata -title: "QST:" +name: Submit Question about: Ask a general question about pytask-stata title: "QST:" labels: "question" ---- +______________________________________________________________________ #### Question about pytask-stata -**Note**: If you'd still like to submit a question, please read [this guide]( -https://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports) detailing how to -provide the necessary information for us to reproduce your question. +**Note**: If you'd still like to submit a question, please read +[this guide](https://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports) +detailing how to provide the necessary information for us to reproduce your question. ```python # Your code here, if applicable diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c3d6d49..aea16c5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -68,10 +68,34 @@ repos: hooks: - id: interrogate args: [-v, --fail-under=40, src, tests] +- repo: https://github.com/executablebooks/mdformat + rev: 0.7.14 + hooks: + - id: mdformat + additional_dependencies: [ + mdformat-gfm, + mdformat-black, + ] + args: [--wrap, "88"] - repo: https://github.com/codespell-project/codespell rev: v2.1.0 hooks: - id: codespell +- repo: https://github.com/pre-commit/mirrors-mypy + rev: 'v0.931' + hooks: + - id: mypy + args: [ + --no-strict-optional, + --ignore-missing-imports, + ] + additional_dependencies: [ + attrs, + click, + types-setuptools + ] + pass_filenames: false + language_version: "3.9" - repo: https://github.com/mgedmin/check-manifest rev: "0.48" hooks: diff --git a/CHANGES.md b/CHANGES.md index cb54a22..016fbe0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,58 +1,51 @@ # Changes -This is a record of all past pytask-stata releases and what went into -them in reverse chronological order. Releases follow [semantic -versioning](https://semver.org/) and all releases are available on -[PyPI](https://pypi.org/project/pytask-stata) and +This is a record of all past pytask-stata releases and what went into them in reverse +chronological order. Releases follow [semantic versioning](https://semver.org/) and all +releases are available on [PyPI](https://pypi.org/project/pytask-stata) and [Anaconda.org](https://anaconda.org/conda-forge/pytask-stata). ## 0.2.0 - 2022-xx-xx -- {pull}`20` removes an unnecessary hook implementation. +- {pull}`18` adds typing to the package. +- {pull}`20` removes an unnecessary hook implementation. ## 0.1.2 - 2022-02-08 -- {pull}`19` fixes the minimum python and - pytask version. +- {pull}`19` fixes the minimum python and pytask version. ## 0.1.1 - 2022-02-07 -- {pull}`16` skips concurrent CI builds. -- {pull}`17` deprecates Python 3.6 and adds - support for Python 3.10. +- {pull}`16` skips concurrent CI builds. +- {pull}`17` deprecates Python 3.6 and adds support for Python 3.10. ## 0.1.0 - 2021-07-21 -- {pull}`11` fixes the `README.rst`. -- {pull}`13` replaces versioneer with - setuptools-scm. -- {pull}`14` fixes tests and aligns - pytask-stata with pytask v0.1.0. +- {pull}`11` fixes the `README.rst`. +- {pull}`13` replaces versioneer with setuptools-scm. +- {pull}`14` fixes tests and aligns pytask-stata with pytask v0.1.0. ## 0.0.6 - 2021-03-05 -- {pull}`10` fixes the version of the package. +- {pull}`10` fixes the version of the package. ## 0.0.5 - 2021-03-04 -- {pull}`7` fix some post-release issues. -- {pull}`9` adds dependencies to `setup.py`. +- {pull}`7` fix some post-release issues. +- {pull}`9` adds dependencies to `setup.py`. ## 0.0.4 - 2021-02-25 -- {pull}`6` prepares pytask-stata to be - published on PyPI, adds versioneer and more. +- {pull}`6` prepares pytask-stata to be published on PyPI, adds versioneer and more. ## 0.0.3 - 2021-01-16 -- {pull}`4` removes log file handling on UNIX - and raises an error if run in parallel. +- {pull}`4` removes log file handling on UNIX and raises an error if run in parallel. ## 0.0.2 - 2020-10-30 -- {pull}`1` makes pytask-stata work with pytask - v0.0.9. +- {pull}`1` makes pytask-stata work with pytask v0.0.9. ## 0.0.1 - 2020-10-04 -- Release v0.0.1. +- Release v0.0.1. diff --git a/README.md b/README.md index affe05d..c7a558a 100644 --- a/README.md +++ b/README.md @@ -10,18 +10,16 @@ [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/pytask-dev/pytask-stata/main.svg)](https://results.pre-commit.ci/latest/github/pytask-dev/pytask-stata/main) [![image](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) ------------------------------------------------------------------------- +______________________________________________________________________ -Run Stata\'s do-files with pytask. +Run Stata's do-files with pytask. ## Installation -pytask-stata is available on -[PyPI](https://pypi.org/project/pytask-stata) and -[Anaconda.org](https://anaconda.org/conda-forge/pytask-stata). Install -it with +pytask-stata is available on [PyPI](https://pypi.org/project/pytask-stata) and +[Anaconda.org](https://anaconda.org/conda-forge/pytask-stata). Install it with -``` console +```console $ pip install pytask-stata # or @@ -29,20 +27,20 @@ $ pip install pytask-stata $ conda install -c conda-forge pytask-stata ``` -You also need to have Stata installed on your system and have the -executable on your system\'s PATH. If you do not know how to do it, -[here](https://superuser.com/a/284351) is an explanation. +You also need to have Stata installed on your system and have the executable on your +system's PATH. If you do not know how to do it, [here](https://superuser.com/a/284351) +is an explanation. ## Usage -Similarly to normal task functions which execute Python code, you define -tasks to execute scripts written in Stata with Python functions. The -difference is that the function body does not contain any logic, but the -decorator tells pytask how to handle the task. +Similarly to normal task functions which execute Python code, you define tasks to +execute scripts written in Stata with Python functions. The difference is that the +function body does not contain any logic, but the decorator tells pytask how to handle +the task. Here is an example where you want to run `script.do`. -``` python +```python import pytask @@ -52,24 +50,23 @@ def task_run_do_file(): pass ``` -When executing a do-file, the current working directory changes to the -directory where the script is located. This allows you, for example, to -reference every data set you want to read with a relative path from the -script. +When executing a do-file, the current working directory changes to the directory where +the script is located. This allows you, for example, to reference every data set you +want to read with a relative path from the script. ### Dependencies and Products -Dependencies and products can be added as with a normal pytask task -using the `@pytask.mark.depends_on` and `@pytask.mark.produces` -decorators. which is explained in this +Dependencies and products can be added as with a normal pytask task using the +`@pytask.mark.depends_on` and `@pytask.mark.produces` decorators. which is explained in +this [tutorial](https://pytask-dev.readthedocs.io/en/stable/tutorials/defining_dependencies_products.html). ### Accessing dependencies and products in the script -The decorator can be used to pass command line arguments to your Stata -executable. For example, pass the path of the product with +The decorator can be used to pass command line arguments to your Stata executable. For +example, pass the path of the product with -``` python +```python @pytask.mark.stata(script="script.do", options="auto.dta") @pytask.mark.produces("auto.dta") def task_run_do_file(): @@ -78,7 +75,7 @@ def task_run_do_file(): And in your `script.do`, you can intercept the value with -``` do +```do * Intercept command line argument and save to macro named 'produces'. args produces @@ -86,14 +83,13 @@ sysuse auto, clear save "`produces'" ``` -The relative path inside the do-file works only because the pytask-stata -switches the current working directory to the directory of the do-file -before the task is executed. +The relative path inside the do-file works only because the pytask-stata switches the +current working directory to the directory of the do-file before the task is executed. -To make the task independent from the current working directory, pass -the full path as an command line argument. Here is an example. +To make the task independent from the current working directory, pass the full path as +an command line argument. Here is an example. -``` python +```python # Absolute path to the build directory. from src.config import BLD @@ -106,14 +102,12 @@ def task_run_do_file(): ### Repeating tasks with different scripts or inputs -You can also parametrize the execution of scripts, meaning executing -multiple do-files as well as passing different command line arguments to -the same do-file. +You can also parametrize the execution of scripts, meaning executing multiple do-files +as well as passing different command line arguments to the same do-file. -The following task executes two do-files which produce different -outputs. +The following task executes two do-files which produce different outputs. -``` python +```python for i in range(2): @pytask.mark.task @@ -129,25 +123,22 @@ pytask-stata can be configured with the following options. *`stata_keep_log`* -Use this option to keep the `.log` files which are produced for -every task. This option is useful to debug Stata tasks. Set the -option via the configuration file with +Use this option to keep the `.log` files which are produced for every task. This option +is useful to debug Stata tasks. Set the option via the configuration file with ```toml [tool.pytask.ini_options] stata_keep_log = true ``` -The option is also available in the command line interface via the -`--stata-keep-log` flag. - +The option is also available in the command line interface via the `--stata-keep-log` +flag. *`stata_check_log_lines`* -Use this option to vary the number of lines in the log file which -are checked for error codes. It also controls the number of lines -displayed on errors. Use any integer greater than zero. Here is the -entry in the configuration file +Use this option to vary the number of lines in the log file which are checked for error +codes. It also controls the number of lines displayed on errors. Use any integer greater +than zero. Here is the entry in the configuration file ```toml [tool.pytask.ini_options] @@ -156,7 +147,7 @@ stata_check_log_lines = 10 and here via the command line interface -``` console +```console $ pytask build --stata-check-log-lines 10 ``` diff --git a/pyproject.toml b/pyproject.toml index 692c35b..35ff30f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,3 +5,20 @@ build-backend = "setuptools.build_meta" [tool.setuptools_scm] write_to = "src/pytask_stata/_version.py" + + +[tool.mypy] +files = ["src", "tests"] +check_untyped_defs = true +disallow_any_generics = true +disallow_incomplete_defs = true +disallow_untyped_defs = true +no_implicit_optional = true +warn_redundant_casts = true +warn_unused_ignores = true + + +[[tool.mypy.overrides]] +module = "tests.*" +disallow_untyped_defs = false +ignore_errors = true diff --git a/src/pytask_stata/collect.py b/src/pytask_stata/collect.py index fa535da..6853faf 100644 --- a/src/pytask_stata/collect.py +++ b/src/pytask_stata/collect.py @@ -5,6 +5,7 @@ import subprocess from pathlib import Path from types import FunctionType +from typing import Any from pytask import depends_on from pytask import has_mark @@ -13,6 +14,7 @@ from pytask import parse_nodes from pytask import produces from pytask import remove_marks +from pytask import Session from pytask import Task from pytask_stata.shared import convert_task_id_to_name_of_log_file from pytask_stata.shared import stata @@ -28,7 +30,9 @@ def run_stata_script( @hookimpl -def pytask_collect_task(session, path, name, obj): +def pytask_collect_task( + session: Session, path: Path, name: str, obj: Any +) -> Task | None: """Perform some checks and prepare the task function.""" __tracebackhide__ = True @@ -59,7 +63,7 @@ def pytask_collect_task(session, path, name, obj): task = Task( base_name=name, path=path, - function=_copy_func(run_stata_script), + function=_copy_func(run_stata_script), # type: ignore depends_on=dependencies, produces=products, markers=markers, @@ -93,14 +97,16 @@ def pytask_collect_task(session, path, name, obj): task.function = stata_function return task + else: + return None -def _parse_stata_mark(mark): +def _parse_stata_mark(mark: Mark) -> Mark: """Parse a Julia mark.""" script, options = stata(**mark.kwargs) parsed_kwargs = {} - for arg_name, value, default in [ + for arg_name, value, default in [ # type: ignore ("script", script, None), ("options", options, []), ]: diff --git a/src/pytask_stata/config.py b/src/pytask_stata/config.py index a37ed2d..b779250 100644 --- a/src/pytask_stata/config.py +++ b/src/pytask_stata/config.py @@ -11,7 +11,11 @@ @hookimpl -def pytask_parse_config(config, config_from_cli, config_from_file): +def pytask_parse_config( + config: dict[str, Any], + config_from_cli: dict[str, Any], + config_from_file: dict[str, Any], +) -> None: """Register the r marker.""" config["markers"]["stata"] = "Tasks which are executed with Stata." config["platform"] = sys.platform @@ -41,7 +45,7 @@ def pytask_parse_config(config, config_from_cli, config_from_file): ) -def _nonnegative_nonzero_integer(x): +def _nonnegative_nonzero_integer(x: Any) -> int: """Check for nonnegative and nonzero integer.""" if x is not None: try: diff --git a/src/pytask_stata/execute.py b/src/pytask_stata/execute.py index 1879bda..db12876 100644 --- a/src/pytask_stata/execute.py +++ b/src/pytask_stata/execute.py @@ -5,12 +5,14 @@ from pytask import has_mark from pytask import hookimpl +from pytask import Session +from pytask import Task from pytask_stata.shared import convert_task_id_to_name_of_log_file from pytask_stata.shared import STATA_COMMANDS @hookimpl -def pytask_execute_task_setup(session, task): +def pytask_execute_task_setup(session: Session, task: Task) -> None: """Check if Stata is found on the PATH.""" if has_mark(task, "stata") and session.config["stata"] is None: raise RuntimeError( @@ -22,7 +24,7 @@ def pytask_execute_task_setup(session, task): @hookimpl -def pytask_execute_task_teardown(session, task): +def pytask_execute_task_teardown(session: Session, task: Task) -> None: """Check if the log file contains no error code. Stata has the weird behavior of always returning an exit code of 0 even if an error diff --git a/src/pytask_stata/parametrize.py b/src/pytask_stata/parametrize.py index 300ee31..d2f49bf 100644 --- a/src/pytask_stata/parametrize.py +++ b/src/pytask_stata/parametrize.py @@ -1,12 +1,15 @@ """Parametrize tasks.""" from __future__ import annotations +from typing import Any +from typing import Callable + import pytask -from pytask import hookimpl +from _pytask.config import hookimpl @hookimpl -def pytask_parametrize_kwarg_to_marker(obj, kwargs): +def pytask_parametrize_kwarg_to_marker(obj: Callable[..., Any], kwargs: Any) -> None: """Attach parametrized stata arguments to the function with a marker.""" if callable(obj): if "stata" in kwargs: diff --git a/src/pytask_stata/plugin.py b/src/pytask_stata/plugin.py index b57f5db..7efaaf1 100644 --- a/src/pytask_stata/plugin.py +++ b/src/pytask_stata/plugin.py @@ -1,7 +1,8 @@ """Register hook specifications and implementations.""" from __future__ import annotations -from pytask import hookimpl +from _pytask.config import hookimpl +from pluggy import PluginManager from pytask_stata import cli from pytask_stata import collect from pytask_stata import config @@ -10,7 +11,7 @@ @hookimpl -def pytask_add_hooks(pm): +def pytask_add_hooks(pm: PluginManager) -> None: """Register hook implementations.""" pm.register(cli) pm.register(collect) diff --git a/src/pytask_stata/shared.py b/src/pytask_stata/shared.py index a188e18..e9f985a 100644 --- a/src/pytask_stata/shared.py +++ b/src/pytask_stata/shared.py @@ -3,6 +3,7 @@ import sys from pathlib import Path +from typing import Any from typing import Iterable from typing import Sequence @@ -71,7 +72,7 @@ def task_r(): def stata( - *args, + *args: Any, script: str | Path | None = None, options: str | Iterable[str] | None = None, ) -> tuple[str | Path | None, str | Iterable[str] | None]: @@ -90,7 +91,7 @@ def stata( return script, options -def convert_task_id_to_name_of_log_file(id_): +def convert_task_id_to_name_of_log_file(id_: str) -> str: """Convert task to id to name of log file. If one passes the complete task id as the log file name, Stata would remove parent @@ -115,12 +116,12 @@ def convert_task_id_to_name_of_log_file(id_): return converted_id -def _to_list(scalar_or_iter): +def _to_list(scalar_or_iter: Any) -> list[Any]: """Convert scalars and iterables to list. Parameters ---------- - scalar_or_iter : str or list + scalar_or_iter Returns -------