diff --git a/docs/CHANGELOG/index.html b/docs/CHANGELOG/index.html index ee3ded5e..8d1887e5 100644 --- a/docs/CHANGELOG/index.html +++ b/docs/CHANGELOG/index.html @@ -1465,7 +1465,16 @@ - + + + + + + 3.0.0 (2024-06-07) + + + + @@ -3843,8 +3852,10 @@ Docs Unreleased⚓︎ +3.0.0 (2024-06-07)⚓︎ Fix⚓︎ +finish migration from autopep8 to ruff resolve remaining ruff errors manually use all-caps LOGGER from corallium require lock before install-extras diff --git a/docs/CODE_TAG_SUMMARY/index.html b/docs/CODE_TAG_SUMMARY/index.html index ad0f319e..227e6d9b 100644 --- a/docs/CODE_TAG_SUMMARY/index.html +++ b/docs/CODE_TAG_SUMMARY/index.html @@ -1495,13 +1495,13 @@ Collected Code Tagscalcipy/check_for_stale_packages/_check_for_stale_packages.py:209 +calcipy/check_for_stale_packages/_check_for_stale_packages.py:220 TODO Consider adding a configuration item for ignore_patterns 2023-02-19 -calcipy/file_search.py:82 +calcipy/file_search.py:88 TODO @@ -1559,7 +1559,7 @@ Collected Code Tags - September 1, 2023 + June 7, 2024 diff --git a/docs/calcipy-banner-wide.svg b/docs/calcipy-banner-wide.svg deleted file mode 100644 index f85e9217..00000000 --- a/docs/calcipy-banner-wide.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - calcipy-banner-wide - Created with Sketch. - - - - - - - - - - - CALCIPY - - - - diff --git a/docs/calcipy-banner.svg b/docs/calcipy-banner.svg deleted file mode 100644 index 304b58bb..00000000 --- a/docs/calcipy-banner.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - calcipy-banner - Created with Sketch. - - - - - - - - - - - CALCIPY - - - - diff --git a/docs/calcipy-square.svg b/docs/calcipy-square.svg deleted file mode 100644 index 6eb5f530..00000000 --- a/docs/calcipy-square.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - calcipy - Created with Sketch. - - - - - - - - - - - - CALCIPY - - - - - diff --git a/modules/calcipy/classes.svg b/modules/calcipy/classes.svg index 692293c6..228bac1e 100644 --- a/modules/calcipy/classes.svg +++ b/modules/calcipy/classes.svg @@ -1,162 +1,162 @@ - - - + + classes - + calcipy.cli.CalcipyConfig - -CalcipyConfig - - -global_defaults(): Dict + +CalcipyConfig + + +global_defaults(): Dict calcipy.tasks._invoke.Collection - -Collection - - -add_task(task: DeferedTask, name: Optional[str], aliases: Optional[Tuple[str, ...]], default: Optional[bool]): None -from_module(module: ModuleType, name: Optional[str], config: Optional[Dict[str, Any]], loaded_from: Optional[str], auto_dash_names: Optional[bool]): 'InvokeCollection' + +Collection + + +add_task(task: DeferedTask, name: Optional[str], aliases: Optional[Tuple[str, ...]], default: Optional[bool]): None +from_module(module: ModuleType, name: Optional[str], config: Optional[Dict[str, Any]], loaded_from: Optional[str], auto_dash_names: Optional[bool]): 'InvokeCollection' calcipy.tasks._invoke.GlobalTaskOptions - -GlobalTaskOptions - -file_args : List[Path] -keep_going : bool -verbose : Annotated -working_dir : Path - - + +GlobalTaskOptions + +file_args : List[Path] +keep_going : bool +verbose : Annotated +working_dir : Path + + calcipy.cli.start_program._CalcipyConfig - -_CalcipyConfig - -gto - - + +_CalcipyConfig + +gto + + calcipy.tasks._invoke.GlobalTaskOptions->calcipy.cli.start_program._CalcipyConfig - - -gto + + +gto calcipy.cli.start_program._CalcipyConfig->calcipy.cli.CalcipyConfig - - + + calcipy.cli._CalcipyProgram - -_CalcipyProgram - - -print_help(): None + +_CalcipyProgram + + +print_help(): None calcipy.code_tag_collector._collector._CodeTag - -_CodeTag - -lineno : int -model_config : ConfigDict -tag : str -text : str - - + +_CodeTag + +lineno : int +model_config +tag : str +text : str + + calcipy.code_tag_collector._collector._CollectorRow - -_CollectorRow - -comment : str -last_edit : str -source_file : str -tag_name : str - -from_code_tag(code_tag: _CodeTag, last_edit: str, source_file: str): '_CollectorRow' + +_CollectorRow + +comment : str +last_edit : str +source_file : str +tag_name : str + +from_code_tag(code_tag: _CodeTag, last_edit: str, source_file: str): '_CollectorRow' calcipy.check_for_stale_packages._check_for_stale_packages._HostedPythonPackage - -_HostedPythonPackage - -datetime : Optional[Arrow] -domain : str -latest_datetime : Optional[Arrow] -latest_version : str -model_config : ConfigDict -name : str -version : str - -date_validator(value: Union[str, Arrow]): Arrow -serialize_datetime(value: Optional[Arrow]): Optional[str] + +_HostedPythonPackage + +datetime : Optional[Arrow] +domain : str +latest_datetime : Optional[Arrow] +latest_version : str +model_config : ConfigDict +name : str +version : str + +date_validator(value: Union[str, Arrow]): Arrow +serialize_datetime(value: Optional[Arrow]): Optional[str] calcipy.md_writer._writer._ParseSkipError - -_ParseSkipError - - - + +_ParseSkipError + + + calcipy.md_writer._writer._ReplacementMachine - -_ReplacementMachine - -state_auto : str -state_user : str - -parse(lines: List[str], handler_lookup: HandlerLookupT, path_file: Path): List[str] + +_ReplacementMachine + +state_auto : str +state_user : str + +parse(lines: List[str], handler_lookup: HandlerLookupT, path_file: Path): List[str] calcipy._RuntimeTypeCheckingModes - -_RuntimeTypeCheckingModes - -name - -from_environment(): Self + +_RuntimeTypeCheckingModes + +name + +from_environment(): Self calcipy.code_tag_collector._collector._Tags - -_Tags - -code_tags : List[_CodeTag] -model_config : ConfigDict -path_source : Path - - + +_Tags + +code_tags : List[_CodeTag] +model_config +path_source : Path + + diff --git a/modules/calcipy/packages.svg b/modules/calcipy/packages.svg index cfc960e5..48d1461a 100644 --- a/modules/calcipy/packages.svg +++ b/modules/calcipy/packages.svg @@ -1,601 +1,601 @@ - - + packages - + calcipy - -calcipy + +calcipy calcipy.can_skip - -calcipy.can_skip + +calcipy.can_skip calcipy.check_for_stale_packages - -calcipy.check_for_stale_packages + +calcipy.check_for_stale_packages calcipy.check_for_stale_packages._check_for_stale_packages - -calcipy.check_for_stale_packages._check_for_stale_packages + +calcipy.check_for_stale_packages._check_for_stale_packages calcipy.check_for_stale_packages->calcipy.check_for_stale_packages._check_for_stale_packages - - + + calcipy.check_for_stale_packages._check_for_stale_packages->calcipy - - + + calcipy.check_for_stale_packages._check_for_stale_packages->calcipy.can_skip - - + + calcipy.cli - -calcipy.cli + +calcipy.cli calcipy.invoke_helpers - -calcipy.invoke_helpers + +calcipy.invoke_helpers calcipy.cli->calcipy.invoke_helpers - - + + calcipy.tasks._invoke - -calcipy.tasks._invoke + +calcipy.tasks._invoke calcipy.cli->calcipy.tasks._invoke - - + + calcipy.code_tag_collector - -calcipy.code_tag_collector + +calcipy.code_tag_collector calcipy.code_tag_collector._collector - -calcipy.code_tag_collector._collector + +calcipy.code_tag_collector._collector calcipy.code_tag_collector->calcipy.code_tag_collector._collector - - + + calcipy.dot_dict - -calcipy.dot_dict + +calcipy.dot_dict calcipy.dot_dict._dot_dict - -calcipy.dot_dict._dot_dict + +calcipy.dot_dict._dot_dict calcipy.dot_dict->calcipy.dot_dict._dot_dict - - + + calcipy.experiments - -calcipy.experiments + +calcipy.experiments calcipy.experiments.bump_programmatically - -calcipy.experiments.bump_programmatically + +calcipy.experiments.bump_programmatically calcipy.experiments.check_duplicate_test_names - -calcipy.experiments.check_duplicate_test_names + +calcipy.experiments.check_duplicate_test_names calcipy.file_search - -calcipy.file_search + +calcipy.file_search calcipy.md_writer - -calcipy.md_writer + +calcipy.md_writer calcipy.md_writer._writer - -calcipy.md_writer._writer + +calcipy.md_writer._writer calcipy.md_writer->calcipy.md_writer._writer - - + + calcipy.md_writer._writer->calcipy.file_search - - + + calcipy.md_writer._writer->calcipy.invoke_helpers - - + + calcipy.noxfile - -calcipy.noxfile + +calcipy.noxfile calcipy.noxfile._noxfile - -calcipy.noxfile._noxfile + +calcipy.noxfile._noxfile calcipy.noxfile->calcipy.noxfile._noxfile - - + + calcipy.scripts - -calcipy.scripts + +calcipy.scripts calcipy.scripts->calcipy.cli - - + + calcipy.tasks - -calcipy.tasks + +calcipy.tasks calcipy.scripts->calcipy.tasks - - + + calcipy.scripts->calcipy.tasks._invoke - - + + calcipy.tasks.all_tasks - -calcipy.tasks.all_tasks + +calcipy.tasks.all_tasks calcipy.scripts->calcipy.tasks.all_tasks - - + + calcipy.tasks.cl - -calcipy.tasks.cl + +calcipy.tasks.cl calcipy.scripts->calcipy.tasks.cl - - + + calcipy.tasks.defaults - -calcipy.tasks.defaults + +calcipy.tasks.defaults calcipy.scripts->calcipy.tasks.defaults - - + + calcipy.tasks.doc - -calcipy.tasks.doc + +calcipy.tasks.doc calcipy.scripts->calcipy.tasks.doc - - + + calcipy.tasks.lint - -calcipy.tasks.lint + +calcipy.tasks.lint calcipy.scripts->calcipy.tasks.lint - - + + calcipy.tasks.pack - -calcipy.tasks.pack + +calcipy.tasks.pack calcipy.scripts->calcipy.tasks.pack - - + + calcipy.tasks.tags - -calcipy.tasks.tags + +calcipy.tasks.tags calcipy.scripts->calcipy.tasks.tags - - + + calcipy.tasks.test - -calcipy.tasks.test + +calcipy.tasks.test calcipy.scripts->calcipy.tasks.test - - + + calcipy.tasks.types - -calcipy.tasks.types + +calcipy.tasks.types calcipy.scripts->calcipy.tasks.types - - + + calcipy.tasks._invoke->calcipy.tasks.types - - + + calcipy.tasks.all_tasks->calcipy.cli - - + + calcipy.tasks.all_tasks->calcipy.tasks._invoke - - + + calcipy.tasks.all_tasks->calcipy.tasks.defaults - - + + calcipy.tasks.cl->calcipy.cli - - + + calcipy.tasks.cl->calcipy.invoke_helpers - - + + calcipy.tasks.executable_utils - -calcipy.tasks.executable_utils + +calcipy.tasks.executable_utils calcipy.tasks.cl->calcipy.tasks.executable_utils - - + + calcipy.tasks.defaults->calcipy.tasks._invoke - - + + calcipy.tasks.doc->calcipy.cli - - + + calcipy.tasks.doc->calcipy.invoke_helpers - - + + calcipy.tasks.doc->calcipy.md_writer - - + + calcipy.tasks.doc->calcipy.tasks.executable_utils - - + + calcipy.tasks.executable_utils->calcipy.invoke_helpers - - + + calcipy.tasks.lint->calcipy.cli - - + + calcipy.tasks.lint->calcipy.invoke_helpers - - + + calcipy.tasks.lint->calcipy.tasks.executable_utils - - + + calcipy.tasks.nox - -calcipy.tasks.nox + +calcipy.tasks.nox calcipy.tasks.nox->calcipy.cli - - + + calcipy.tasks.nox->calcipy.invoke_helpers - - + + calcipy.tasks.nox->calcipy.tasks.executable_utils - - + + calcipy.tasks.pack->calcipy - - + + calcipy.tasks.pack->calcipy.can_skip - - + + calcipy.tasks.pack->calcipy.cli - - + + calcipy.tasks.pack->calcipy.experiments - - + + calcipy.tasks.pack->calcipy.experiments.bump_programmatically - - + + calcipy.tasks.pack->calcipy.invoke_helpers - - + + calcipy.tasks.pack->calcipy.tasks.executable_utils - - + + calcipy.tasks.stale - -calcipy.tasks.stale + +calcipy.tasks.stale calcipy.tasks.stale->calcipy.check_for_stale_packages - - + + calcipy.tasks.stale->calcipy.cli - - + + calcipy.tasks.tags->calcipy.cli - - + + calcipy.tasks.tags->calcipy.code_tag_collector - - + + calcipy.tasks.tags->calcipy.file_search - - + + calcipy.tasks.tags->calcipy.invoke_helpers - - + + calcipy.tasks.tags->calcipy.tasks.defaults - - + + calcipy.tasks.test->calcipy.cli - - + + calcipy.tasks.test->calcipy.experiments - - + + calcipy.tasks.test->calcipy.experiments.check_duplicate_test_names - - + + calcipy.tasks.test->calcipy.invoke_helpers - - + + calcipy.tasks.test->calcipy.tasks.defaults - - + + calcipy.tasks.test->calcipy.tasks.executable_utils - - + + calcipy.tasks.types->calcipy.cli - - + + calcipy.tasks.types->calcipy.invoke_helpers - - + + calcipy.tasks.types->calcipy.tasks.executable_utils - - + + diff --git a/search/search_index.json b/search/search_index.json index 2a3a00c4..56e1a1b1 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"calcipy","text":"calcipy is a Python package that implements best practices such as code style (linting, auto-fixes), documentation, CI/CD, and logging. Like the calcium carbonate in hard coral, packages can be built on the calcipy foundation. calcipy has some configurability, but is tailored for my particular use cases. If you want the same sort of functionality, there are a number of alternatives to consider: pyscaffold is a much more mature project that aims for the same goals, but with a slightly different approach and tech stack (tox vs. nox, cookiecutter vs. copier, etc.) tidypy, pylama, and codecheck offer similar functionality of bundling and running static checkers, but makes far fewer assumptions pytoil is a general CLI tool for developer automation And many more such as pyta, prospector, wemake-python-styleguide / cjolowicz/cookiecutter-hypermodern-python, formate, johnthagen/python-blueprint, oxsecurity/megalinter, etc. "},{"location":"#installation","title":"Installation","text":"Calcipy needs a few static files managed using copier and a template project: kyleking/calcipy_template You can quickly use the template to create a new project or add calcipy to an existing one: # Install copier. pipx is recommended\npipx install copier\n\n# To create a new project\ncopier copy gh:KyleKing/calcipy_template new_project\ncd new_project\n\n# Or convert/update an existing one\ncd my_project\ncopier copy gh:KyleKing/calcipy_template .\ncopier update\n"},{"location":"#calcipy-cli","title":"Calcipy CLI","text":"Additionally, calcipy can be run as a CLI application without adding the package as a dependency. Quick Start: # For the CLI, only install a few of the extras which can be used from a few different CLI commands\npipx install 'calcipy[lint,tags]'\n\n# Use 'tags' to create a CODE_TAG_SUMMARY of the specified directory\ncalcipy-tags tags --help\ncalcipy-tags tags --base-dir=~/path/to/my_project\n\n# You can list all provided CLI commands with\npipx list\n venvs are in ~/.local/pipx/venvs\napps are exposed on your $PATH at ~/.local/bin\n package calcipy 1.4.0, installed using Python 3.11.4\n - calcipy\n - calcipy-lint\n - calcipy-pack\n - calcipy-tags\n - calcipy-types\n Note: the CLI output below is compressed for readability, but you can try running each of these commands locally to see the most up-to-date documentation and the full set of options. The \u201cUsage\u201d, \u201cCore options\u201d, and \u201cGlobal Task Options\u201d are the same for each subsequent command, so they are excluded for brevity. > calcipy-lint\nUsage: calcipy-lint [--core-opts] <subcommand> [--subcommand-opts] ...\n\nCore options:\n\n --complete Print tab-completion candidates for given parse remainder.\n --hide=STRING Set default value of run()'s 'hide' kwarg.\n --print-completion-script=STRING Print the tab-completion script for your preferred shell (bash|zsh|fish).\n --prompt-for-sudo-password Prompt user at start of session for the sudo.password config value.\n --write-pyc Enable creation of .pyc files.\n -d, --debug Enable debug output.\n -D INT, --list-depth=INT When listing tasks, only show the first INT levels.\n -e, --echo Echo executed commands before running.\n -f STRING, --config=STRING Runtime configuration file to use.\n -F STRING, --list-format=STRING Change the display format used when listing tasks. Should be one of: flat (default), nested, json.\n -h [STRING], --help[=STRING] Show core or per-task help and exit.\n -l [STRING], --list[=STRING] List available tasks, optionally limited to a namespace.\n -p, --pty Use a pty when executing shell commands.\n -R, --dry Echo commands instead of running.\n -T INT, --command-timeout=INT Specify a global command execution timeout, in seconds.\n -V, --version Show version and exit.\n -w, --warn-only Warn, instead of failing, when shell commands fail.\n\nSubcommands:\n\n lint.autopep8 Run autopep8.\n lint.check (lint) Run ruff as check-only.\n lint.fix Run ruff and apply fixes.\n lint.pre-commit Run pre-commit.\n lint.pylint Run pylint.\n lint.security Attempt to identify possible security vulnerabilities.\n lint.watch Run ruff as check-only.\n\nGlobal Task Options:\n\n *file_args List of Paths available globally to all tasks. Will resolve paths with working_dir\n --keep-going Continue running tasks even on failure\n --working_dir=STRING Set the cwd for the program. Example: \"../run --working-dir .. lint test\"\n -v,-vv,-vvv Globally configure logger verbosity (-vvv for most verbose)\n\n> calcipy-pack\n\nSubcommands:\n\n pack.check-licenses Check licenses for compatibility with `licensecheck`.\n pack.install-extras Run poetry install with all extras.\n pack.lock Ensure poetry.lock is up-to-date.\n pack.publish Build the distributed format(s) and publish.\n\n> calcipy-tags\n\nSubcommands:\n\n tags.collect-code-tags (tags) Create a `CODE_TAG_SUMMARY.md` with a table for TODO- and FIXME-style code comments.\n\n> calcipy-types\n\nSubcommands:\n\n types.basedpyright Run basedpyright.\n types.mypy Run mypy.\n types.pyright Run pyright.\n"},{"location":"#calcipy-pre-commit","title":"Calcipy Pre-Commit","text":"calcipy can also be used as a pre-commit task by adding the below snippet to your pre-commit file: repos:\n - repo: https://github.com/KyleKing/calcipy\n rev: main\n hooks:\n - id: tags\n - id: lint-fix\n - id: types\n"},{"location":"#project-status","title":"Project Status","text":"See the Open Issues and/or the CODE_TAG_SUMMARY. For release history, see the CHANGELOG."},{"location":"#contributing","title":"Contributing","text":"We welcome pull requests! For your pull request to be accepted smoothly, we suggest that you first open a GitHub issue to discuss your idea. For resources on getting started with the code base, see the below documentation: DEVELOPER_GUIDE STYLE_GUIDE "},{"location":"#code-of-conduct","title":"Code of Conduct","text":"We follow the Contributor Covenant Code of Conduct."},{"location":"#open-source-status","title":"Open Source Status","text":"We try to reasonably meet most aspects of the \u201cOpenSSF scorecard\u201d from Open Source Insights"},{"location":"#responsible-disclosure","title":"Responsible Disclosure","text":"If you have any security issue to report, please contact the project maintainers privately. You can reach us at dev.act.kyle@gmail.com."},{"location":"#license","title":"License","text":"LICENSE"},{"location":"docs/CHANGELOG/","title":"Docs","text":""},{"location":"docs/CHANGELOG/#unreleased","title":"Unreleased","text":""},{"location":"docs/CHANGELOG/#fix","title":"Fix","text":" resolve remaining ruff errors manually use all-caps LOGGER from corallium require lock before install-extras copier-auto-update "},{"location":"docs/CHANGELOG/#refactor","title":"Refactor","text":" unsafe fixes from ruff safe fixes from ruff "},{"location":"docs/CHANGELOG/#220rc0-2024-06-06","title":"2.2.0rc0 (2024-06-06)","text":""},{"location":"docs/CHANGELOG/#feat","title":"Feat","text":" remove bandit/security task remove flake8 "},{"location":"docs/CHANGELOG/#fix_1","title":"Fix","text":" make experimental dependencies optional remove pip-check shorten main task list remove autopep8 remove semgrep "},{"location":"docs/CHANGELOG/#210-2024-04-17","title":"2.1.0 (2024-04-17)","text":""},{"location":"docs/CHANGELOG/#feat_1","title":"Feat","text":" add basedpyright "},{"location":"docs/CHANGELOG/#fix_2","title":"Fix","text":" exclude plantuml package 1.10.0 "},{"location":"docs/CHANGELOG/#refactor_1","title":"Refactor","text":" replace filter with iterator "},{"location":"docs/CHANGELOG/#204-2024-01-31","title":"2.0.4 (2024-01-31)","text":""},{"location":"docs/CHANGELOG/#fix_3","title":"Fix","text":" add \u2013doc-sub-dir for Code Tag generation without copier-answers create the code tag summary directory if it doesn\u2019t already exist "},{"location":"docs/CHANGELOG/#203-2023-11-21","title":"2.0.3 (2023-11-21)","text":""},{"location":"docs/CHANGELOG/#fix_4","title":"Fix","text":" pin a recent version of virtualenv for Python 3.12 + Nox "},{"location":"docs/CHANGELOG/#202-2023-11-08","title":"2.0.2 (2023-11-08)","text":""},{"location":"docs/CHANGELOG/#refactor_2","title":"Refactor","text":" copier-auto-update "},{"location":"docs/CHANGELOG/#201-2023-09-08","title":"2.0.1 (2023-09-08)","text":""},{"location":"docs/CHANGELOG/#fix_5","title":"Fix","text":" add missing setuptools for some mkdocs dependencies add calcipy-docs and restore missing extras for type checking add scoped \u2018calcipy-test\u2019 "},{"location":"docs/CHANGELOG/#refactor_3","title":"Refactor","text":" really move nox use ruff.external to allow list flake8 rules "},{"location":"docs/CHANGELOG/#200-2023-09-02","title":"2.0.0 (2023-09-02)","text":""},{"location":"docs/CHANGELOG/#feat_2","title":"Feat","text":" drop Python 3.8 "},{"location":"docs/CHANGELOG/#fix_6","title":"Fix","text":" specify a unique name when binding new kwargs "},{"location":"docs/CHANGELOG/#164-2023-08-28","title":"1.6.4 (2023-08-28)","text":""},{"location":"docs/CHANGELOG/#fix_7","title":"Fix","text":" update pyrate-limiter "},{"location":"docs/CHANGELOG/#refactor_4","title":"Refactor","text":" address pydantic v2 migration warnings "},{"location":"docs/CHANGELOG/#163-2023-08-13","title":"1.6.3 (2023-08-13)","text":""},{"location":"docs/CHANGELOG/#fix_8","title":"Fix","text":" remove redundant beartype to lru_cache "},{"location":"docs/CHANGELOG/#refactor_5","title":"Refactor","text":" resolve asdf tried get_env_vars "},{"location":"docs/CHANGELOG/#162-2023-08-12","title":"1.6.2 (2023-08-12)","text":""},{"location":"docs/CHANGELOG/#161rc0-2023-08-12","title":"1.6.1rc0 (2023-08-12)","text":""},{"location":"docs/CHANGELOG/#fix_9","title":"Fix","text":" support partial kwargs for pre/post defer Task to support decorator chaining "},{"location":"docs/CHANGELOG/#refactor_6","title":"Refactor","text":" setup for deferred task refactor update dependency versions update copier for a more generic environment variable "},{"location":"docs/CHANGELOG/#160-2023-07-23","title":"1.6.0 (2023-07-23)","text":""},{"location":"docs/CHANGELOG/#feat_3","title":"Feat","text":" experiment with griffe for version bumping "},{"location":"docs/CHANGELOG/#fix_10","title":"Fix","text":" resolve minor bugs in griffe experiment remove dependency on flake8-beartype "},{"location":"docs/CHANGELOG/#refactor_7","title":"Refactor","text":" downgrade replacement machine error to warning "},{"location":"docs/CHANGELOG/#150-2023-07-22","title":"1.5.0 (2023-07-22)","text":""},{"location":"docs/CHANGELOG/#feat_4","title":"Feat","text":" add configurable ignore patterns for tags "},{"location":"docs/CHANGELOG/#refactor_8","title":"Refactor","text":" resolve pylint warnings run bump-pydantic "},{"location":"docs/CHANGELOG/#142-2023-07-16","title":"1.4.2 (2023-07-16)","text":""},{"location":"docs/CHANGELOG/#fix_11","title":"Fix","text":" use the correct exclude rule for semgrep support pydantic v2 serialization skip SemGrep rule to pin GitHub Actions to commit IDs "},{"location":"docs/CHANGELOG/#refactor_9","title":"Refactor","text":" resolve local test suite problems "},{"location":"docs/CHANGELOG/#141-2023-07-07","title":"1.4.1 (2023-07-07)","text":""},{"location":"docs/CHANGELOG/#fix_12","title":"Fix","text":" retrieve only unique python versions from .tool-versions "},{"location":"docs/CHANGELOG/#140-2023-06-25","title":"1.4.0 (2023-06-25)","text":""},{"location":"docs/CHANGELOG/#feat_5","title":"Feat","text":" add calcipy-pack to use install-extras without the extras "},{"location":"docs/CHANGELOG/#fix_13","title":"Fix","text":" show a readable warning when gh, pyright, and/or pre-commit are not installed use calcipy\u2019s python environment when installed with pipx "},{"location":"docs/CHANGELOG/#130-2023-06-21","title":"1.3.0 (2023-06-21)","text":""},{"location":"docs/CHANGELOG/#feat_6","title":"Feat","text":" switch to httpx for async requests "},{"location":"docs/CHANGELOG/#perf","title":"Perf","text":" lazy cache compiling the regex "},{"location":"docs/CHANGELOG/#126-2023-06-17","title":"1.2.6 (2023-06-17)","text":""},{"location":"docs/CHANGELOG/#fix_14","title":"Fix","text":" handle missing git remotes in code tag collection always re-cache packages if empty ignore new TD002 & TD003 linting rules "},{"location":"docs/CHANGELOG/#refactor_10","title":"Refactor","text":" try to handle exceptions when no git origin URL "},{"location":"docs/CHANGELOG/#125-2023-05-23","title":"1.2.5 (2023-05-23)","text":""},{"location":"docs/CHANGELOG/#fix_15","title":"Fix","text":" resolve typing and import errors upgrade to invoke 2.1.2 "},{"location":"docs/CHANGELOG/#refactor_11","title":"Refactor","text":" suppress ruff autofix "},{"location":"docs/CHANGELOG/#124-2023-05-16","title":"1.2.4 (2023-05-16)","text":""},{"location":"docs/CHANGELOG/#fix_16","title":"Fix","text":" add pack.install-extras bump minimum pymdown dependency pydantic alias doesn\u2019t work like that refactor for higher pylint quality update the help string to include \u2013keep-going support branch coverage move pyright to \u2018main\u2019 tasks "},{"location":"docs/CHANGELOG/#123-2023-04-08","title":"1.2.3 (2023-04-08)","text":""},{"location":"docs/CHANGELOG/#fix_17","title":"Fix","text":" handle dev installs when only calcipy "},{"location":"docs/CHANGELOG/#122-2023-04-08","title":"1.2.2 (2023-04-08)","text":""},{"location":"docs/CHANGELOG/#fix_18","title":"Fix","text":" add preview of \u2013keep-going "},{"location":"docs/CHANGELOG/#refactor_12","title":"Refactor","text":" extract task logic from wrapper "},{"location":"docs/CHANGELOG/#121-2023-04-07","title":"1.2.1 (2023-04-07)","text":""},{"location":"docs/CHANGELOG/#fix_19","title":"Fix","text":" skip assert_used in bandit "},{"location":"docs/CHANGELOG/#120-2023-04-06","title":"1.2.0 (2023-04-06)","text":""},{"location":"docs/CHANGELOG/#feat_7","title":"Feat","text":" add test.check with duplicate name logic "},{"location":"docs/CHANGELOG/#fix_20","title":"Fix","text":" remove lru_cache, which was causing mypy errors "},{"location":"docs/CHANGELOG/#111-2023-04-06","title":"1.1.1 (2023-04-06)","text":""},{"location":"docs/CHANGELOG/#fix_21","title":"Fix","text":" resolve linting errors use copier copy "},{"location":"docs/CHANGELOG/#110-2023-04-06","title":"1.1.0 (2023-04-06)","text":""},{"location":"docs/CHANGELOG/#feat_8","title":"Feat","text":" add experimental check duplicate test names "},{"location":"docs/CHANGELOG/#fix_22","title":"Fix","text":" suppress unexpected exit errors in update docs skip pre-commit uninstall for doc.deploy when pre-commit not available skip pre-commit on doc.deploy "},{"location":"docs/CHANGELOG/#101-2023-03-01","title":"1.0.1 (2023-03-01)","text":""},{"location":"docs/CHANGELOG/#fix_23","title":"Fix","text":" bump minimum ruff version "},{"location":"docs/CHANGELOG/#100-2023-02-25","title":"1.0.0 (2023-02-25)","text":""},{"location":"docs/CHANGELOG/#fix_24","title":"Fix","text":" lower default logging level "},{"location":"docs/CHANGELOG/#100rc7-2023-02-23","title":"1.0.0rc7 (2023-02-23)","text":""},{"location":"docs/CHANGELOG/#feat_9","title":"Feat","text":" add lint.security to pre-commit lint-fix hook "},{"location":"docs/CHANGELOG/#fix_25","title":"Fix","text":" remove types hook, which doesn\u2019t work when not local "},{"location":"docs/CHANGELOG/#refactor_13","title":"Refactor","text":" make task summary optional configure start program inline "},{"location":"docs/CHANGELOG/#100rc6-2023-02-22","title":"1.0.0rc6 (2023-02-22)","text":""},{"location":"docs/CHANGELOG/#fix_26","title":"Fix","text":" provide subcommands to support pre-commit "},{"location":"docs/CHANGELOG/#100rc5-2023-02-22","title":"1.0.0rc5 (2023-02-22)","text":""},{"location":"docs/CHANGELOG/#feat_10","title":"Feat","text":" add pre-commit hooks and update docs "},{"location":"docs/CHANGELOG/#fix_27","title":"Fix","text":" handle KeyError when not yet on PyPi\u2019s JSON API "},{"location":"docs/CHANGELOG/#refactor_14","title":"Refactor","text":" sync with corallium ruff and remove flake8 class-attr-order "},{"location":"docs/CHANGELOG/#100rc4-2023-02-22","title":"1.0.0rc4 (2023-02-22)","text":""},{"location":"docs/CHANGELOG/#fix_28","title":"Fix","text":" fully support nox and corallium "},{"location":"docs/CHANGELOG/#100rc3-2023-02-22","title":"1.0.0rc3 (2023-02-22)","text":""},{"location":"docs/CHANGELOG/#fix_29","title":"Fix","text":" add depedency on corallium "},{"location":"docs/CHANGELOG/#100rc2-2023-02-21","title":"1.0.0rc2 (2023-02-21)","text":""},{"location":"docs/CHANGELOG/#feat_11","title":"Feat","text":" merge shoal source code into calcipy "},{"location":"docs/CHANGELOG/#100rc1-2023-02-21","title":"1.0.0rc1 (2023-02-21)","text":""},{"location":"docs/CHANGELOG/#fix_30","title":"Fix","text":" upgrade shoal dependency and add summary add pylint to document move autopep8 to the lint extras "},{"location":"docs/CHANGELOG/#100rc0-2023-02-20","title":"1.0.0rc0 (2023-02-20)","text":""},{"location":"docs/CHANGELOG/#feat_12","title":"Feat","text":" fully support working-dir by wrapping ctx.run recursively find the tool versions file add dont_skip for testing add tests for code tag collector replace dg with user configuration file better support min-cover unit test write_autoformatted_md_sections start using check_imports support prereleases and add autopep8 introduce cl namespace run lint on file_args and use absolufy add initial version of doc namespace remove pre-commit; add doc and ddict extras add progress indicator to tasks add lint.security add ./run to generlize local entrypoint add lint.pre-commit restore most of noxfile run mypy-silent and add main task use can_skip for the package_lock file start manually fixing lint issues add linting migrate over stale package logic add pyright, mypy, and DEFAULTS add write_json and cached pyproject reader add pytest-based tasks and first unit tests experiment with parallel processing init placeholders for new namespaces add can_skip add initial nox task to invoke switch to invoke start restoring files and switching to shoal add shoal "},{"location":"docs/CHANGELOG/#fix_31","title":"Fix","text":" correct ignore pattern logic and expand task tests get doc and cl tasks working code tag summary and rename default tasks properly map CLI arguments to code tag collector show all URLs on the same line in CTC write Code Tags to docs/docs resolve issues with test.watch-json "},{"location":"docs/CHANGELOG/#refactor_15","title":"Refactor","text":" update Progress task extract use_pty to a default in shoal generlize find_in_parents for pyproject.toml as well introduce logger.print and print_debug drop absolufy-imports task simplify configuration fix remaining pyright and flake8 errors rename write-json to coverage resolve pylint issues reorder main tasks resolve flake8 warnings and better manage filenames apply autopep8 to fix whitespace issues import initialized logger directly try to wrap running tasks programmatically\u2026 run prc main move log configuration to shoal.cli.task resolve typecheck errors manually resolve additional linting errors make lint.check default extract start program to shoal another round of manual lint corrections move can_skip to shoal nest invoke-specific code in tasks/ drop pytest-html and conftest completely "},{"location":"docs/CHANGELOG/#0216-2023-01-31","title":"0.21.6 (2023-01-31)","text":""},{"location":"docs/CHANGELOG/#feat_13","title":"Feat","text":" use the \u2013now flag for ptw! "},{"location":"docs/CHANGELOG/#refactor_16","title":"Refactor","text":" remove autoflake in favor of unimport "},{"location":"docs/CHANGELOG/#0215-2022-11-27","title":"0.21.5 (2022-11-27)","text":""},{"location":"docs/CHANGELOG/#fix_32","title":"Fix","text":" escape % in yaml files as well "},{"location":"docs/CHANGELOG/#0214-2022-11-20","title":"0.21.4 (2022-11-20)","text":""},{"location":"docs/CHANGELOG/#fix_33","title":"Fix","text":" handle % in path names for ctt suppress code tag failures when not a git directory add support for tomllib in Python 3.11! "},{"location":"docs/CHANGELOG/#refactor_17","title":"Refactor","text":" copier update "},{"location":"docs/CHANGELOG/#0213-2022-11-13","title":"0.21.3 (2022-11-13)","text":""},{"location":"docs/CHANGELOG/#fix_34","title":"Fix","text":" remove unimplemented diff-cover exit 1 on semgrep errors "},{"location":"docs/CHANGELOG/#0212-2022-11-12","title":"0.21.2 (2022-11-12)","text":""},{"location":"docs/CHANGELOG/#fix_35","title":"Fix","text":" add relative path prefix for linting \u2018./\u2019 ensure that cz bump doesn\u2019t break the requirements file bump minimum pip-check "},{"location":"docs/CHANGELOG/#0211-2022-11-05","title":"0.21.1 (2022-11-05)","text":""},{"location":"docs/CHANGELOG/#fix_36","title":"Fix","text":" install poetry for \u2018doit publish\u2019 bump minimum mkdocs for deploy_docs "},{"location":"docs/CHANGELOG/#0210-2022-11-05","title":"0.21.0 (2022-11-05)","text":""},{"location":"docs/CHANGELOG/#feat_14","title":"Feat","text":" remove if-expr and other unnecessary new flake8 plugins merge extra flake8 packages based on flake8-aggressive "},{"location":"docs/CHANGELOG/#fix_37","title":"Fix","text":" prevent line break in table for Code Tag summary add pdbr with rich to replace pdbpp "},{"location":"docs/CHANGELOG/#0201-2022-10-19","title":"0.20.1 (2022-10-19)","text":""},{"location":"docs/CHANGELOG/#fix_38","title":"Fix","text":" drop pdbpp and add flake8-fine-pytest "},{"location":"docs/CHANGELOG/#0200-2022-10-16","title":"0.20.0 (2022-10-16)","text":""},{"location":"docs/CHANGELOG/#feat_15","title":"Feat","text":" sign commitizen tags add plantuml generation to mkdocs "},{"location":"docs/CHANGELOG/#fix_39","title":"Fix","text":" remove version conflict with flake8-simplify and cl_bump "},{"location":"docs/CHANGELOG/#0191-2022-10-13","title":"0.19.1 (2022-10-13)","text":""},{"location":"docs/CHANGELOG/#fix_40","title":"Fix","text":" don\u2019t auto-install types "},{"location":"docs/CHANGELOG/#0190-2022-10-06","title":"0.19.0 (2022-10-06)","text":""},{"location":"docs/CHANGELOG/#feat_16","title":"Feat","text":" support optional logging of arguments "},{"location":"docs/CHANGELOG/#fix_41","title":"Fix","text":" split mypy install from mypy run "},{"location":"docs/CHANGELOG/#0180-2022-09-27","title":"0.18.0 (2022-09-27)","text":""},{"location":"docs/CHANGELOG/#feat_17","title":"Feat","text":" support Arrow in pydantic and mypy (ArrowType was causing errors) remove pdoc(s) from document task add gen_ref_nav! tried pybetter, but too focused on adding all #102: sort-of-replace pdocs, but pdoc only shows one function add mypy install argument expand flake8 and reduce tests "},{"location":"docs/CHANGELOG/#fix_42","title":"Fix","text":" re-run prdc lower fail-under for diff. Raise for regular test standardize on a single doit task list Pathlib.absolute is not documented. Use .resolve "},{"location":"docs/CHANGELOG/#refactor_18","title":"Refactor","text":" add type hints and minor changes for mypy make python files non-executable (chmod -x) "},{"location":"docs/CHANGELOG/#0171-2022-09-22","title":"0.17.1 (2022-09-22)","text":""},{"location":"docs/CHANGELOG/#fix_43","title":"Fix","text":" pytest cache assert circular reference "},{"location":"docs/CHANGELOG/#refactor_19","title":"Refactor","text":" move check_security from nox into doit "},{"location":"docs/CHANGELOG/#0170-2022-09-17","title":"0.17.0 (2022-09-17)","text":""},{"location":"docs/CHANGELOG/#feat_18","title":"Feat","text":" add docformat make pyupgrade flag configurable based on minimum python version replace pendulum with arrow update to latest copier "},{"location":"docs/CHANGELOG/#fix_44","title":"Fix","text":" move yamllint configuration to project-specific config naive datetimes can\u2019t be subtracted on Windows hack together a dictionary instead of punq drop safety because of false positives on Calcipy CalVer "},{"location":"docs/CHANGELOG/#refactor_20","title":"Refactor","text":" experiment with custom semgrep rules formalize solution from last commit try to fix punq by switching to functions fix one test, refactor a little, but punq is still not working begin replacing attrs with pydantic update semgrep nox task upgrade copier with linting fixes "},{"location":"docs/CHANGELOG/#0160-2022-08-04","title":"0.16.0 (2022-08-04)","text":""},{"location":"docs/CHANGELOG/#feat_19","title":"Feat","text":" add flake8-simplify "},{"location":"docs/CHANGELOG/#fix_45","title":"Fix","text":" show only date in coverage table "},{"location":"docs/CHANGELOG/#0150-2022-08-03","title":"0.15.0 (2022-08-03)","text":""},{"location":"docs/CHANGELOG/#fix_46","title":"Fix","text":" resolve pyroma errors by installing poetry as a module "},{"location":"docs/CHANGELOG/#0150rc0-2022-07-24","title":"0.15.0rc0 (2022-07-24)","text":""},{"location":"docs/CHANGELOG/#feat_20","title":"Feat","text":" init update workflow and toml sorter create script to bump toml minimum requirements "},{"location":"docs/CHANGELOG/#fix_47","title":"Fix","text":" properly handle missing datetime try to use separate sqlite files for pre-commit and doit correct safety arguments use the unversioned API for releases try to handle pypi response when no \u201creleases\u201d handle prefix of \u201c*\u201d improve error message when \u201creleases\u201d not present use sqlite3 for pre-commit concurrent doit access bump Python to 3.8.4 and drop 3.7 "},{"location":"docs/CHANGELOG/#0145-2022-03-05","title":"0.14.5 (2022-03-05)","text":""},{"location":"docs/CHANGELOG/#fix_48","title":"Fix","text":" #91: prevent minified files from appearing in Code Tag Summary "},{"location":"docs/CHANGELOG/#0144-2022-03-01","title":"0.14.4 (2022-03-01)","text":""},{"location":"docs/CHANGELOG/#fix_49","title":"Fix","text":" set upper limit to fix flake8-bandit compat "},{"location":"docs/CHANGELOG/#0143-2022-03-01","title":"0.14.3 (2022-03-01)","text":""},{"location":"docs/CHANGELOG/#fix_50","title":"Fix","text":" suppress only known git blame errors use Github tables to prevent multi-line rows in Code Tag Summary "},{"location":"docs/CHANGELOG/#0142-2022-02-27","title":"0.14.2 (2022-02-27)","text":""},{"location":"docs/CHANGELOG/#fix_51","title":"Fix","text":" don\u2019t allow multi-line tables "},{"location":"docs/CHANGELOG/#0141-2022-02-27","title":"0.14.1 (2022-02-27)","text":""},{"location":"docs/CHANGELOG/#fix_52","title":"Fix","text":" lower pandas constraint for better 3.7 support drop generation of requirements.txt "},{"location":"docs/CHANGELOG/#0140-2022-02-27","title":"0.14.0 (2022-02-27)","text":""},{"location":"docs/CHANGELOG/#feat_21","title":"Feat","text":" use next-gen attrs syntax improve code tag regex "},{"location":"docs/CHANGELOG/#fix_53","title":"Fix","text":" correct type annotations undo silent nox step make coverage.json optional for task_doc "},{"location":"docs/CHANGELOG/#0130-2022-02-23","title":"0.13.0 (2022-02-23)","text":""},{"location":"docs/CHANGELOG/#feat_22","title":"Feat","text":" drop AppVeyor and update with copier #67: initialize GH CI Action "},{"location":"docs/CHANGELOG/#fix_54","title":"Fix","text":" drop appveyor from cz version files attempt to resolve UnicodeError on Windows action address more minor CI errors make yamllint more relaxed attempt to resolve CI issues remove diff-quality check until fixed for CI always use Interactive when using /dev/null run push hook stage and format subproject support Python 3.7.12 don\u2019t suppress nox missing interpreter errors "},{"location":"docs/CHANGELOG/#refactor_21","title":"Refactor","text":" pre-commit can be run in one command resolve LGTM error "},{"location":"docs/CHANGELOG/#0122-2022-02-21","title":"0.12.2 (2022-02-21)","text":""},{"location":"docs/CHANGELOG/#fix_55","title":"Fix","text":" skip cassette files "},{"location":"docs/CHANGELOG/#0121-2022-02-18","title":"0.12.1 (2022-02-18)","text":""},{"location":"docs/CHANGELOG/#fix_56","title":"Fix","text":" always return 0 to complete format task use short paths when formatting Python code if available "},{"location":"docs/CHANGELOG/#0120-2022-02-18","title":"0.12.0 (2022-02-18)","text":""},{"location":"docs/CHANGELOG/#feat_23","title":"Feat","text":" initialize calcipy pre-commit hook "},{"location":"docs/CHANGELOG/#0110-2022-02-18","title":"0.11.0 (2022-02-18)","text":""},{"location":"docs/CHANGELOG/#feat_24","title":"Feat","text":" don\u2019t verify on cz_bump "},{"location":"docs/CHANGELOG/#refactor_22","title":"Refactor","text":" format fixes reduce noise on matched vulnerabilities make run_cmd public remove jsonlint action use beartype typing move pre-commit hooks into doit "},{"location":"docs/CHANGELOG/#0100-2022-01-19","title":"0.10.0 (2022-01-19)","text":""},{"location":"docs/CHANGELOG/#feat_25","title":"Feat","text":" create Github release if gh CLI is installed "},{"location":"docs/CHANGELOG/#091-2022-01-17","title":"0.9.1 (2022-01-17)","text":""},{"location":"docs/CHANGELOG/#fix_57","title":"Fix","text":" correct code-tag-collector for CLI use use target file\u2019s directory for git info "},{"location":"docs/CHANGELOG/#refactor_23","title":"Refactor","text":" drop template echo command "},{"location":"docs/CHANGELOG/#090-2022-01-17","title":"0.9.0 (2022-01-17)","text":""},{"location":"docs/CHANGELOG/#feat_26","title":"Feat","text":" show code tag summary as a table add date of last blame to code tag summary only link code tag from line number use revision-specific commit hashes add git links to code tag summary add python format pre-commit hook add support for TOML formatting with taplo "},{"location":"docs/CHANGELOG/#fix_58","title":"Fix","text":" handle \u201c0000..\u201d hash by using branch name handle non-git directories use correct line number for pinned hash handle git dependencies when checking stale packages use positional arguments for pre-commit commands correct syntax error in pre-commit command "},{"location":"docs/CHANGELOG/#refactor_24","title":"Refactor","text":" make slow python pre-commit push-only apply auto-format tools to test project "},{"location":"docs/CHANGELOG/#082-2022-01-16","title":"0.8.2 (2022-01-16)","text":""},{"location":"docs/CHANGELOG/#fix_59","title":"Fix","text":" drop all references to CalVer "},{"location":"docs/CHANGELOG/#081-2022-01-16","title":"0.8.1 (2022-01-16)","text":""},{"location":"docs/CHANGELOG/#refactor_25","title":"Refactor","text":" drop tag_format and use semver-only "},{"location":"docs/CHANGELOG/#080-2022-01-16","title":"0.8.0 (2022-01-16)","text":""},{"location":"docs/CHANGELOG/#feat_27","title":"Feat","text":" add pip-check for prettier outdated add cct command initialize the cement CLI application replace toml with tomli (#74) move wily to nox to reduce version conflicts add pip-audit add attrs_strict trim trailing whitespace from doc output "},{"location":"docs/CHANGELOG/#fix_60","title":"Fix","text":" bump year to 2022 code_tag_collector must return doit-compliant value (None) only run pre-commit once Code Tag Collector cannot return a Path for DoIt tasks cct was not being created as an alias. Use full command wily properly exclude auto-generated doc files from code tag collector run ptw on whole directory, not just test files drop FYI and NOTE from Code Tags "},{"location":"docs/CHANGELOG/#refactor_26","title":"Refactor","text":" fix PY-W2000 by using all move file_search to top-level just run pre-commit install replace pass with \u2026 to keep it from being removed "},{"location":"docs/CHANGELOG/#060rc2-2022-08-04","title":"0.6.0rc2 (2022-08-04)","text":""},{"location":"docs/CHANGELOG/#feat_28","title":"Feat","text":" drop pytest-cov and run coverage directly "},{"location":"docs/CHANGELOG/#fix_61","title":"Fix","text":" swap pytest-watch(er) and other version bumps pin min-version (xenon) & drop preconvert and flake8-mock "},{"location":"docs/CHANGELOG/#060rc1-2022-08-04","title":"0.6.0rc1 (2022-08-04)","text":""},{"location":"docs/CHANGELOG/#fix_62","title":"Fix","text":" bug in noxfile and code tags reading doc output files "},{"location":"docs/CHANGELOG/#060rc0-2022-08-04","title":"0.6.0rc0 (2022-08-04)","text":""},{"location":"docs/CHANGELOG/#feat_29","title":"Feat","text":" implement pylint rules add xenon (wraps radon) condense doit output in show_cmd initial semgrep implementation add code diagrams to doc-site add notes on wily and pylint add vulture diff-quality (lint) add it add output if no stale packages found add autoflake "},{"location":"docs/CHANGELOG/#fix_63","title":"Fix","text":" try to fix issues found with pytest_cache_assert drop unused Python2 demjson error in noxfile with poetry install "},{"location":"docs/CHANGELOG/#refactor_27","title":"Refactor","text":" decouple code tag collector from DG improve lint_py and the return type check is working zip release task cleanup a few minor errors use Interactive instead of Chrome apply fixes found from semgrep rename \u201cwrappers\u201d to \u201cdot_dict\u201d prevent mutation in DG.set_paths "},{"location":"docs/CHANGELOG/#031rc0-2022-08-04","title":"0.3.1rc0 (2022-08-04)","text":""},{"location":"docs/CHANGELOG/#feat_30","title":"Feat","text":" add task to zip artifacts from testing implement pandas.to_markdown for cov table #58: implement doc task and merge serve_docs make loading YAML files more generic create doit summary report add task to check license compliance move lock into doit tasks with file-dependency #38: re-implement coverage and write source drop subprocess-tee #36: WIP implementation of doc formatting implement publish tasks add pip outdated to stale check implement check for stale packages begin implementing stale package check smart default tasks for CI vs. local make nox imports optional try doit rather than CWD for initial path move noxfile into calcipy integrate nox to doit tasks WIP check for stale dep & placeholder publish add additional configuration options add nox tasks add detect-secrets as pre-commit lint non-Python files read doc_dir from copier file make most settings configurable added dotted-dict wrapper of Box delete old files rather than full directory filter with glob-like ignore_patterns (2/2) add security check task add skip_phrase for code tag skipping beartype - roar! optionally clear log directory. Move file_helpers from base new tail-like reverse read_lines rename DIG to DG because doit is one word retrieve doc_dir from copier add log-setup fun for doit make (source) doc_dir configurable add sanitizer add pytype #36: start WIP ReadMeMachine #36: merge logic of markdown auto-formatters make code tag partially configurable move check_types into calcipy import templates from pdocs "},{"location":"docs/CHANGELOG/#fix_64","title":"Fix","text":" install full dev-dep as temporary nox workaround prevent committing changelog at base dir remove type_name from docstring for Google-style run pytest as a module for nox resolve doc_dir and style errors #58: remove None from pdocs output deconflict doit/nox repair failing test packaging needed keyword argument import and nox syntax errors run push pre-commit hooks with doit show log warning instead of error for stale dep improve output from check_stale errors caught in testing suppress beartype warnings for now drop logger middleware catch most calcipy section typos in toml remove check-secrets but keep snippets errors in ignore patterns import doit and expand nox test file skip tags must be at bottom reset Lint paths instead of extend use find_files for code tag summary don\u2019t manually add the package source dir lint typing and improve tests create separate file_search file to fix imports #51: replace glob with git-based file identification #53: Use Interact instead of LongRunning undo PEP585 for runtime beartype resolve file explosion from _find_files import the loguru Logger class safely additional problems found with mypy fix Doit Types and start beartype add no-verify to cl_bump correct isort configuration restore ReadMeMachine repair small bugs found in tests make transitions optional "},{"location":"docs/CHANGELOG/#refactor_28","title":"Refactor","text":" see if only one space is okay for skipcq fix anti-pattern with nox session decorator and arg improve code quality try to suppress deepsource errors address DeepSource issues split up set_paths from DG fix formatting error from pre-push rename doc_dir to doc_sub_dir for clarity apply 0.0.10 template move markdown to subdirectory for mkdocs move isort back to toml fix edge case in diff-cov failing and lint errors relicense with MIT for better compliance create generic doit-runner for noxfile fix some type issues minor fixes from AppVeyor testing #38: reduce complexity minor cleanup to docs apply pre-commit autoformat auto-drop skipcq comment format with VSCode fix lint errors in YAML files move find file paths to DG.meta move if_found_unlink to file_helpers fix formatting with pre-commit fix attribute names for path types (1/2) use calcipy:skip-tags fix minor code tags move __temp_chdir and improve fix_dg narrow type ignore use use path_file of file_path apply auto-fixes from pre-commit replace all glob-search with find_files remove find_files from code_tag script update isort for trailing-comma move excluded lint rules to DG apply PEP585 and add pre-commit hook fix capitalization for doit (one word, all lowercase) improve activate_debug_logging try to replace Any with BaseAction apply 0.0.2 minor fixes apply 0.0.1 version of calcipy_template minor renaming for _MarkdownMachine fix lock file and line length run more pre-commit checks on commit standardize on code tags and cleanup "},{"location":"docs/CHANGELOG/#perf_1","title":"Perf","text":" combine autopep8 paths into single command combine files for linting in one command "},{"location":"docs/CHANGELOG/#020a0-2022-08-04","title":"0.2.0a0 (2022-08-04)","text":""},{"location":"docs/CHANGELOG/#feat_31","title":"Feat","text":" push tags with no pre-commit hooks on pre use Interactive instead of \u2014yes for cl_bump* remove tag create/remove tasks new task cl_bump_pre replace MIT license with Unlicense use cz_legacy to generate changelogs remove task in anticipation of copier #26 improve git pre-commit hooks new add-trailing-comma and pyupgrade hooks new mkdocs tasks and improvements new optional preconvert to serialize logs move logger configuration to log_helpers new cl_bump task. Closes #21 "},{"location":"docs/CHANGELOG/#fix_65","title":"Fix","text":" do not pass filenames to pre-commit #43: add year to version for pseudo-calver prevent legacy types for new commits prevent circular import in doit_tasks extras need to be defined as optional rollback hook changes as they are not working install hooks for push LongRunning passed tasks that should fail yesqa removed necessary noqa (H303, etc) incorrect output paths reduce false tags found (WIP). Fix #24 regression in lint_project tasks unincremented version in toml "},{"location":"docs/CHANGELOG/#refactor_29","title":"Refactor","text":" #22: restore MIT license copy+paste unmodified labels workflow update local TODO notes make toml an optional import rename path_source to path_project rename DIG_CWD as PATH_TEST_PROJECT rename test file doit is lowercase (CC looks like Dolt) replace sh with subprocess-tee reduce excess logging move DOIT_CONFIG to import move dig test to dig test file "},{"location":"docs/CHANGELOG/#010-2020-12-19","title":"0.1.0 (2020-12-19)","text":""},{"location":"docs/CHANGELOG/#feat_32","title":"Feat","text":" new write_cl task utilizing comittizen new task, pre_commit_hooks "},{"location":"docs/CHANGELOG/#refactor_30","title":"Refactor","text":" show STDOUT formatting in DoIt task remove archived code "},{"location":"docs/CHANGELOG/#003-2020-12-10","title":"0.0.3 (2020-12-10)","text":""},{"location":"docs/CHANGELOG/#002-2020-11-14","title":"0.0.2 (2020-11-14)","text":""},{"location":"docs/CHANGELOG/#001-2020-11-14","title":"0.0.1 (2020-11-14)","text":""},{"location":"docs/CODE_TAG_SUMMARY/","title":"Collected Code Tags","text":"Type Comment Last Edit Source File TODO If no stale, write out five oldest? 2023-05-13 calcipy/check_for_stale_packages/_check_for_stale_packages.py:209 TODO Consider adding a configuration item for ignore_patterns 2023-02-19 calcipy/file_search.py:82 TODO Look into running tasks from within other tasks to support \u2018\u2013continue\u2019 and more readable logs \u2013> 2023-02-19 docs/docs/MIGRATION.md:112 TODO Capture logging output and check\u2026 2023-02-19 tests/check_for_stale_packages/test_check_for_stale_packages.py:64 TODO Capture logging output and check\u2026 2023-02-19 tests/check_for_stale_packages/test_check_for_stale_packages.py:88 TODO Is there an easier way to maintain pytest parameter IDs? 2023-02-17 tests/tasks/test_test.py:14 PLANNED Convert to hypothesis test! 2023-02-19 tests/test_dot_dict.py:9 Found code tags for TODO (6), PLANNED (1)"},{"location":"docs/DEVELOPER_GUIDE/","title":"Developer Notes","text":""},{"location":"docs/DEVELOPER_GUIDE/#local-development","title":"Local Development","text":"git clone https://github.com/kyleking/calcipy.git\ncd calcipy\npoetry install --sync\npoetry run calcipy-pack pack.install-extras\n\n# See the available tasks\npoetry run calcipy\n# Or use a local 'run' file (so that 'calcipy' can be extended)\n./run\n\n# Run the default task list (lint, auto-format, test coverage, etc.)\n./run main\n\n# Make code changes and run specific tasks as needed:\n./run lint.fix test\n"},{"location":"docs/DEVELOPER_GUIDE/#publishing","title":"Publishing","text":"For testing, create an account on TestPyPi. Replace ... with the API token generated on TestPyPi or PyPi respectively poetry config repositories.testpypi https://test.pypi.org/legacy/\npoetry config pypi-token.testpypi ...\n\n./run main pack.publish --to-test-pypi\n# If you didn't configure a token, you will need to provide your username and password to publish\n To publish to the real PyPi poetry config pypi-token.pypi ...\n./run release\n\n# Or for a pre-release\n./run release --suffix=rc\n"},{"location":"docs/DEVELOPER_GUIDE/#current-status","title":"Current Status","text":"File Statements Missing Excluded Coverage calcipy/__init__.py 16 0 24 100.0% calcipy/can_skip.py 17 1 0 89.3% calcipy/check_for_stale_packages/__init__.py 5 2 0 60.0% calcipy/check_for_stale_packages/_check_for_stale_packages.py 118 8 3 87.2% calcipy/cli.py 35 1 78 93.0% calcipy/code_tag_collector/__init__.py 5 2 0 60.0% calcipy/code_tag_collector/_collector.py 143 2 0 94.0% calcipy/dot_dict/__init__.py 5 2 0 60.0% calcipy/dot_dict/_dot_dict.py 8 0 0 100.0% calcipy/experiments/__init__.py 0 0 0 100.0% calcipy/experiments/bump_programmatically.py 24 24 0 0.0% calcipy/experiments/check_duplicate_test_names.py 36 0 2 95.0% calcipy/file_search.py 38 0 2 91.8% calcipy/invoke_helpers.py 30 2 0 81.8% calcipy/md_writer/__init__.py 5 2 0 60.0% calcipy/md_writer/_writer.py 95 6 0 88.9% calcipy/noxfile/__init__.py 5 2 0 60.0% calcipy/noxfile/_noxfile.py 44 2 51 83.8% calcipy/scripts.py 5 0 37 100.0% calcipy/tasks/__init__.py 0 0 0 100.0% calcipy/tasks/_invoke.py 34 0 55 97.6% calcipy/tasks/all_tasks.py 48 0 0 95.5% calcipy/tasks/cl.py 28 5 0 75.0% calcipy/tasks/defaults.py 20 0 0 89.3% calcipy/tasks/doc.py 45 0 8 90.5% calcipy/tasks/executable_utils.py 27 0 0 87.2% calcipy/tasks/lint.py 44 2 0 82.9% calcipy/tasks/nox.py 8 0 0 100.0% calcipy/tasks/pack.py 42 11 0 64.1% calcipy/tasks/stale.py 6 0 0 100.0% calcipy/tasks/tags.py 18 1 0 91.7% calcipy/tasks/test.py 45 1 2 89.2% calcipy/tasks/types.py 20 0 0 89.3% Totals 1019 76 262 86.5% Generated on: 2024-06-07"},{"location":"docs/MIGRATION/","title":"Migration Guide","text":""},{"location":"docs/MIGRATION/#calcipy-100","title":"calcipy 1.0.0","text":""},{"location":"docs/MIGRATION/#background","title":"Background","text":"calcipy v1 was a complete rewrite to switch from doit to invoke: with invoke, tasks can be run from anywhere without a dodo.py file tasks can be loaded lazily, which means that some performance gains are possible since there is no shared state file, tasks can be more easily run from pre-commit or generally in parallel doit excelled at clearly delineated task output and run summary, but invoke isn\u2019t designed that way. I would like to improve the CLI output, but the benefits are worth this tradeoff. calcipy v0 was built on doit and thus required a dodo.py file. I began adding cement to support a separate CLI for calcipy installed with pipx, but that required a lot of boilerplate code. With doit, the string command needed to be complete at task evaluation rather than runtime, so globbing files couldn\u2019t be resolved lazily."},{"location":"docs/MIGRATION/#migration","title":"Migration","text":"While refactoring, the global configuration was mostly removed (DoitGlobals) along with a few tasks, but the main functionality is still present. Any project dependent on calcipy will need substantial changes. The easiest way to start migrating is to run copier copy gh:KyleKing/calcipy_template . for calcipy_template"},{"location":"docs/MIGRATION/#speed-test","title":"Speed Test","text":"It turns out that switching to invoke appears to have only saved 100ms > hyperfine -m 20 --warmup 5 \"poetry run python -c 'print(1)'\"\nBenchmark 1: poetry run python -c 'print(1)'\nTime (mean \u00b1 \u03c3): 377.9 ms \u00b1 3.1 ms [User: 235.0 ms, System: 61.8 ms]\nRange (min \u2026 max): 372.7 ms \u2026 384.0 ms 20 runs\n> hyperfine -m 20 --warmup 5 ./run\nBenchmark 1: ./run\nTime (mean \u00b1 \u03c3): 936.0 ms \u00b1 26.9 ms [User: 1548.2 ms, System: 1687.7 ms]\nRange (min \u2026 max): 896.4 ms \u2026 1009.4 ms 20 runs\n> hyperfine -m 20 --warmup 5 \"poetry run calcipy_tags\"\nBenchmark 1: poetry run calcipy_tags\nTime (mean \u00b1 \u03c3): 618.5 ms \u00b1 29.7 ms [User: 1536.8 ms, System: 1066.2 ms]\nRange (min \u2026 max): 578.2 ms \u2026 694.9 ms 20 runs\n> hyperfine -m 20 --warmup 5 \"poetry run doit list\"\nBenchmark 1: poetry run doit list\nTime (mean \u00b1 \u03c3): 1.002 s \u00b1 0.015 s [User: 1.643 s, System: 1.682 s]\nRange (min \u2026 max): 0.974 s \u2026 1.023 s 20 runs\n Additionally, the major decrease in dependencies will make install and update actions much faster. With the recommended extras installed, calcipy-v1 has 124 dependencies (with all extras, 164) vs. calcipy-v0\u2019s 259. Counted with: cat .calcipy_packaging.lock | jq 'keys' | wc -l"},{"location":"docs/MIGRATION/#code-comparison","title":"Code Comparison","text":"Accounting for code extracted to corallium, the overall number of lines decreased from 1772 to 1550 or only 12%, while increasing the CLI and pre-commit capabilities. ~/calcipy-v0 > cloc calcipy\n-------------------------------------------------------------------------------\nLanguage files blank comment code\n-------------------------------------------------------------------------------\nPython 26 942 1075 1772\n-------------------------------------------------------------------------------\nSUM: 26 942 1075 1772\n-------------------------------------------------------------------------------\n~/calcipy > cloc calcipy\n-------------------------------------------------------------------------------\nLanguage files blank comment code\n-------------------------------------------------------------------------------\nPython 27 454 438 1185\n-------------------------------------------------------------------------------\nSUM: 27 454 438 1185\n-------------------------------------------------------------------------------\n~/corallium > cloc corallium\n-------------------------------------------------------------------------------\nLanguage files blank comment code\n-------------------------------------------------------------------------------\nPython 7 176 149 365\n-------------------------------------------------------------------------------\nSUM: 7 176 149 365\n-------------------------------------------------------------------------------\n\n~/calcipy > cloc tests\n-------------------------------------------------------------------------------\nLanguage files blank comment code\n-------------------------------------------------------------------------------\nYAML 2 0 0 580\nPython 19 176 68 578\nJSON 2 0 0 60\nMarkdown 3 9 10 8\nText 1 0 0 2\n-------------------------------------------------------------------------------\nSUM: 27 185 78 1228\n-------------------------------------------------------------------------------\n~/calcipy-v0 > cloc tests\n-------------------------------------------------------------------------------\nLanguage files blank comment code\n-------------------------------------------------------------------------------\nJSON 30 0 0 762\nYAML 2 0 0 580\nPython 24 314 186 578\nMarkdown 3 9 10 8\n-------------------------------------------------------------------------------\nSUM: 59 323 196 1928\n-------------------------------------------------------------------------------\n~/corallium > cloc tests\n-------------------------------------------------------------------------------\nLanguage files blank comment code\n-------------------------------------------------------------------------------\nPython 6 36 15 69\nMarkdown 1 1 0 2\n-------------------------------------------------------------------------------\nSUM: 7 37 15 71\n-------------------------------------------------------------------------------\n"},{"location":"docs/MIGRATION/#doit-output","title":"doit output","text":"I would like to restore the doit task summary, but invoke\u2019s architecture doesn\u2019t really make this possible. The --continue option was extremely useful, but that also might not be achievable. > poetry run doit run\n. format_recipes > [\n Python: function format_recipes\n]\n\n2023-02-19 10:40:23.954 | INFO | recipes.formatter:_write_toc:287 - Creating TOC for: ./recipes/docs/breakfast\n2023-02-19 10:40:23.957 | INFO | recipes.formatter:_write_toc:287 - Creating TOC for: ./recipes/docs/rice\n2023-02-19 10:40:23.959 | INFO | recipes.formatter:_write_toc:287 - Creating TOC for: ./recipes/docs/meals\n2023-02-19 10:40:23.964 | INFO | recipes.formatter:_write_toc:287 - Creating TOC for: ./recipes/docs/seafood\n2023-02-19 10:40:23.967 | INFO | recipes.formatter:_write_toc:287 - Creating TOC for: ./recipes/docs/pizza\n2023-02-19 10:40:23.969 | INFO | recipes.formatter:_write_toc:287 - Creating TOC for: ./recipes/docs/poultry\n2023-02-19 10:40:23.972 | INFO | recipes.formatter:_write_toc:287 - Creating TOC for: ./recipes/docs/sushi\n. collect_code_tags > [\n Python: function write_code_tag_file\n]\n\n. cl_write > [\n Cmd: poetry run cz changelog\n Python: function _move_cl\n]\n\n. lock > [\n Cmd: poetry lock --no-update\n]\n\nResolving dependencies...\n. nox_coverage > [\n Cmd: poetry run nox --error-on-missing-interpreters --session coverage\n]\n\n...\n\ndoit> Summary:\ndoit> format_recipes was successful\ndoit> collect_code_tags was successful\ndoit> cl_write was successful\ndoit> lock was successful\ndoit> nox_coverage was successful\ndoit> auto_format was successful\ndoit> document was successful\ndoit> check_for_stale_packages was successful\ndoit> pre_commit_hooks failed (red)\ndoit> lint_project was not run\ndoit> static_checks was not run\ndoit> security_checks was not run\ndoit> check_types was not run\n"},{"location":"docs/STYLE_GUIDE/","title":"Personal Style Guides","text":""},{"location":"docs/STYLE_GUIDE/#git","title":"Git","text":"We use Commitizen to manage both an auto-generated Changelog and incrementing the release version following semver. For both of these automated outputs to work well, please follow the Conventional Commits style, which is described in more detail below."},{"location":"docs/STYLE_GUIDE/#commitizen-types-and-scopes","title":"Commitizen Types and Scopes","text":"type(scope): description Types fix: A bug fix feat: A new feature docs: Documentation-only changes (code comments, separate docs) style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons) perf: A code change that improves performance refactor: A change to production code that is not a fix, feat, or perf test: Adding missing or correcting existing tests build: Changes that affect the build system or external dependencies ci: Changes to our CI configuration files and scripts A ! can be used to indicate a breaking change (refactor!: drop support for Node 6) SemVer Rules Based on commit type, the version will be auto-incremented: fix : PATCH // feat : MINOR // BREAKING CHANGE : MAJOR Scopes A Class, File name, Issue Number, other appropriate noun. As examples: build(poetry): bump requests to v3 or style(#32): add missing type annotations Tips What if a commit fits multiple types? Go back and make multiple commits whenever possible. Part of the benefit of Conventional Commits is the focus on more organized and intentional changes Use git rebase -i to fix commit names prior to merging if incorrect types/scopes are used "},{"location":"docs/STYLE_GUIDE/#git-description-guidelines","title":"Git Description Guidelines","text":" Commit message guidelines Full sentence with verb (lowercase) and concise description. Below are modified examples for Conventional Commits fix(roles): bug in admin role permissions feat(ui): implement new button design build(pip): upgrade package to remove vulnerabilities refactor: file structure to improve code readability perf(cli): rewrite methods feat(api): endpoints to implement new customer dashboard How to write a good commit message A diff will tell you what changed, but only the commit message can properly tell you why. Keep in mind: This has all been said before. From the seven rules of a great Git commit message: (2) Try for 50 characters, but consider 72 the hard limit (7) Use the body to explain what and why vs. how "},{"location":"docs/STYLE_GUIDE/#issue-labels-and-milestones","title":"Issue Labels and Milestones","text":"Personal Guide For Issue Labels, see [labels.yml][labels] Milestones Current Tasks: main milestone (name could change based on a specific project, sprint, or month) Next Tasks Blue Sky Research [Sane GitHub Labels](https://medium.com/@dave_lunny/sane-github-labels-c5d2e6004b63) and see [sensible-github-labels](https://github.com/Relequestual/sensible-github-labels) for full descriptions of each \u201cit is much more helpful to see the status and type of all issues at a glance.\u201d One of each: Status: \u2026 Abandoned, Accepted, Available, Blocked, Completed, In Progress, On Hold, Pending, Review Needed, Revision Needed Type: \u2026 Bug, Maintenance, Question, Enhancement Priority: \u2026 Critical, High, Medium, Low [Britecharts](https://britecharts.github.io/britecharts/github-labels.html) Status: \u2026 On Review \u2013 Request that we are pondering if including or not Needs Reproducing \u2013 For bugs that need to be reproduced in order to get fixed Needs Design \u2013 Feature that needs a design Ready to Go \u2013 Issue that has been defined and is ready to get started with In Progress \u2013 Issue that is being worked on right now. Completed \u2013 Finished feature or fix Type: \u2026 Bug \u2013 An unexpected problem or unintended behavior Feature \u2013 A new feature request Maintenance \u2013 A regular maintenance chore or task, including refactors, build system, CI, performance improvements Documentation \u2013 A documentation improvement task Question \u2013 An issue or PR that needs more information or a user question Not Included Priority: They would add complexity and overhead due to the discussions, but could help with the roadmap Technology Labels: It can create too much overhead, as properly tag with technologies all the issues could be time consuming [Ian Bicking Blog](https://www.ianbicking.org/blog/2014/03/use-github-issues-to-organize-a-project.html) Milestone Overview What are we doing right now? What aren\u2019t we doing right now? 2a. Stuff we\u2019ll probably do soon 2b. Stuff we probably won\u2019t do soon What aren\u2019t we sure about? Milestone Descriptions Stuff we are doing right now: this is the \u201cmain\u201d milestone. We give it a name (like Alpha 2 or Strawberry Rhubarb Pie) and we write down what we are trying to accomplish with the milestone. We create a new milestone when we are ready for the next iteration. Stuff we\u2019ll probably do soon: this is a standing \u201c**Next Tasks**\u201d milestone. We never change or rename this milestone. We use a permanent \u201cNext Tasks\u201d milestone (as opposed to renaming it to \u201cAlpha 3\u201d or actual-next-iteration milestone) because we don\u2019t want to presume or default to including something in the real next iteration. When we\u2019re ready to start planning the next iteration we\u2019ll create a new milestone, and only deliberately move things into that milestone. Stuff we probably won\u2019t do soon: this is a standing \u201c**Blue Sky**\u201d milestone. We refer to these tickets and sometimes look through them, but they are easy to ignore, somewhat intentionally ignored. What aren\u2019t we sure about?: issues with no milestone. Label: \u201cNeeds Discussion\u201d - (addressed in a triage meeting) - use liberally for either big or small tickets \u201cIt\u2019s better to give people more power: it\u2019s actually helpful if people can overreach because it is an opportunity to establish where the limits really are and what purpose those limits have\u201d "},{"location":"docs/STYLE_GUIDE/#external-links","title":"External Links","text":" [Git: The Simple Guide][simple_git] [Commit Messages][gcmsg] and why use the present tense GitHub\u2019s Advice on GitHub Most Comprehensive Guide Git Pro Book (free) Bash Tab-Completion Snippet "},{"location":"docs/STYLE_GUIDE/#python","title":"Python","text":" Python Style Guides https://gist.github.com/sloria/7001839 http://www.nilunder.com/blog/2013/08/03/pythonic-sensibilities/ https://innoq.github.io/cards42org_en/ https://docs.openstack.org/hacking/latest/user/hacking.html#styleguide https://www.python.org/doc/humor/ https://docs.python-guide.org/writing/reading/ https://realpython.com/python-refactoring/ "},{"location":"docs/STYLE_GUIDE/#adrs","title":"ADRs","text":" ADR Approaches https://infraeng.dev/tech-spec Template (And associated review) vs. https://infraeng.dev/decision-log/ Y-Statements: abbreviated shorthand. Add this as a one-line decision option if a full ADR isn\u2019t needed (or when referencing an existing ADR) (https://scribe.rip/@docsoc/y-statements-10eb07b5a177) https://adr.github.io More formal implementation of ADRs (MADR) that this is based on. Template: https://github.com/adr/madr/blob/97fb8edec60b8dc70b8166ef62de34c4e26b46c0/template/adr-template.md https://github.com/ethereum/EIPs/blob/confluenceuser/EIPS/eip-5639.md Examples https://github.com/pawamoy/mkdocstrings/issues/28 https://github.com/arachne-framework/architecture/blob/060a956277a5ad71df93da49fee52463408841af/adr-002-configuration.md https://github.com/arachne-framework/architecture/tree/060a956277a5ad71df93da49fee52463408841af https://github.com/ethereum/EIPs/blob/confluenceuser/EIPS/eip-1010.md https://docs-v1.prefect.io/core/pins/pin-01-introduce-pins.html https://peps.python.org/pep-0387/ https://github.com/AICoE/aicoe-ci/blob/39de02af86a0d1f9dcd395fa88b858f1c6880411/docs/adr/0000-use-markdown-architectural-decision-records.md And many others! <\u2013 Links \u2013>"},{"location":"modules/calcipy/_code_diagrams/","title":"Code Diagrams","text":"Auto-generated with pylint-pyreverse"},{"location":"modules/calcipy/_code_diagrams/#packages","title":"Packages","text":"Full Size"},{"location":"modules/calcipy/_code_diagrams/#classes","title":"Classes","text":"Full Size"},{"location":"reference/SUMMARY/","title":"SUMMARY","text":" calcipy can_skip check_for_stale_packages _check_for_stale_packages cli code_tag_collector _collector dot_dict _dot_dict experiments bump_programmatically check_duplicate_test_names file_search invoke_helpers md_writer _writer noxfile _noxfile scripts tasks _invoke all_tasks cl defaults doc executable_utils lint nox pack stale tags test types "},{"location":"reference/calcipy/","title":"calcipy","text":"calcipy."},{"location":"reference/calcipy/#calcipy-functions","title":"Functions","text":""},{"location":"reference/calcipy/#calcipy.configure_runtime_type_checking_mode","title":"configure_runtime_type_checking_mode","text":"configure_runtime_type_checking_mode()\n Optionally configure runtime type checking mode globally. Source code in calcipy/__init__.py def configure_runtime_type_checking_mode() -> None: # pragma: no cover\n \"\"\"Optionally configure runtime type checking mode globally.\"\"\"\n rtc_mode = _RuntimeTypeCheckingModes.from_environment()\n\n if rtc_mode is not _RuntimeTypeCheckingModes.OFF:\n from beartype.roar import BeartypeClawDecorWarning # noqa: PLC0415\n\n beartype_this_package(conf=BeartypeConf(\n warning_cls_on_decorator_exception=(\n None if rtc_mode is _RuntimeTypeCheckingModes.ERROR else BeartypeClawDecorWarning\n ),\n ))\n"},{"location":"reference/calcipy/can_skip/","title":"can_skip","text":"Support can-skip logic from Make."},{"location":"reference/calcipy/can_skip/#calcipy.can_skip-functions","title":"Functions","text":""},{"location":"reference/calcipy/can_skip/#calcipy.can_skip.can_skip","title":"can_skip","text":"can_skip(*, prerequisites, targets)\n Return true if the prerequisite files are have newer mtime than targets. Example use with Invoke, but can be used anywhere: @task()\ndef test(ctx: Context) -> None:\n if can_skip(prerequisites=[*Path('src').rglob('*.py')], targets=[Path('.coverage.xml')]):\n return # Exit early\n\n ... # Task code\n Source code in calcipy/can_skip.py @beartype\ndef can_skip(*, prerequisites: List[Path], targets: List[Path]) -> bool:\n \"\"\"Return true if the prerequisite files are have newer `mtime` than targets.\n\n Example use with Invoke, but can be used anywhere:\n\n ```py\n @task()\n def test(ctx: Context) -> None:\n if can_skip(prerequisites=[*Path('src').rglob('*.py')], targets=[Path('.coverage.xml')]):\n return # Exit early\n\n ... # Task code\n ```\n\n \"\"\"\n if not (ts_prerequisites := [pth.stat().st_mtime for pth in prerequisites]):\n raise ValueError('Required files do not exist', prerequisites)\n\n ts_targets = [pth.stat().st_mtime for pth in targets]\n if ts_targets and min(ts_targets) > max(ts_prerequisites):\n LOGGER.warning('Skipping because targets are newer', targets=targets)\n return True\n return False\n"},{"location":"reference/calcipy/can_skip/#calcipy.can_skip.dont_skip","title":"dont_skip","text":"dont_skip(*, prerequisites, targets)\n To use for testing with mock; always returns False. Source code in calcipy/can_skip.py @beartype\ndef dont_skip(*, prerequisites: List[Path], targets: List[Path]) -> bool:\n \"\"\"To use for testing with mock; always returns False.\"\"\"\n LOGGER.debug('Mocking can_skip', prerequisites=prerequisites, targets=targets)\n return False\n"},{"location":"reference/calcipy/cli/","title":"cli","text":"Extend Invoke for Calcipy."},{"location":"reference/calcipy/cli/#calcipy.cli-classes","title":"Classes","text":""},{"location":"reference/calcipy/cli/#calcipy.cli.CalcipyConfig","title":"CalcipyConfig","text":" Bases: Config Opinionated Config with better defaults. Source code in calcipy/cli.py class CalcipyConfig(Config):\n \"\"\"Opinionated Config with better defaults.\"\"\"\n\n @staticmethod\n def global_defaults() -> Dict: # type: ignore[type-arg] # pragma: no cover\n \"\"\"Override the global defaults.\"\"\"\n invoke_defaults = Config.global_defaults()\n calcipy_defaults = {\n 'run': {\n 'echo': True,\n 'echo_format': '\\033[2;3;37mRunning: {command}\\033[0m',\n 'pty': use_pty(),\n },\n }\n return merge_dicts(invoke_defaults, calcipy_defaults)\n"},{"location":"reference/calcipy/cli/#calcipy.cli.CalcipyConfig-functions","title":"Functions","text":""},{"location":"reference/calcipy/cli/#calcipy.cli.CalcipyConfig.global_defaults","title":"global_defaults staticmethod","text":"global_defaults()\n Override the global defaults. Source code in calcipy/cli.py @staticmethod\ndef global_defaults() -> Dict: # type: ignore[type-arg] # pragma: no cover\n \"\"\"Override the global defaults.\"\"\"\n invoke_defaults = Config.global_defaults()\n calcipy_defaults = {\n 'run': {\n 'echo': True,\n 'echo_format': '\\033[2;3;37mRunning: {command}\\033[0m',\n 'pty': use_pty(),\n },\n }\n return merge_dicts(invoke_defaults, calcipy_defaults)\n"},{"location":"reference/calcipy/cli/#calcipy.cli-functions","title":"Functions","text":""},{"location":"reference/calcipy/cli/#calcipy.cli.start_program","title":"start_program","text":"start_program(pkg_name, pkg_version, module=None, collection=None)\n Run the customized Invoke Program. FYI: recommendation is to extend the core_args method, but this won\u2019t parse positional arguments: https://docs.pyinvoke.org/en/stable/concepts/library.html#modifying-core-parser-arguments Source code in calcipy/cli.py @beartype\ndef start_program(\n pkg_name: str,\n pkg_version: str,\n module: Optional[ModuleType] = None,\n collection: Optional[Union[Collection, InvokeCollection]] = None,\n) -> None: # pragma: no cover\n \"\"\"Run the customized Invoke Program.\n\n FYI: recommendation is to extend the `core_args` method, but this won't parse positional arguments:\n https://docs.pyinvoke.org/en/stable/concepts/library.html#modifying-core-parser-arguments\n\n \"\"\"\n # Manipulate 'sys.argv' to hide arguments that invoke can't parse\n _gto = GlobalTaskOptions()\n sys_argv: List[str] = sys.argv[:1]\n last_argv = ''\n for argv in sys.argv[1:]:\n if not last_argv.startswith('-') and Path(argv).is_file():\n _gto.file_args.append(Path(argv))\n # Check for CLI flags\n elif argv in {'-v', '-vv', '-vvv', '--verbose'}:\n _gto.verbose = argv.count('v')\n elif argv == '--keep-going':\n _gto.keep_going = True\n # Check for CLI arguments with values\n elif last_argv == '--working-dir':\n _gto.working_dir = Path(argv).resolve()\n elif argv != '--working-dir':\n sys_argv.append(argv)\n last_argv = argv\n _gto.file_args = [\n _f if _f.is_absolute() else Path.cwd() / _f\n for _f in _gto.file_args\n ]\n sys.argv = sys_argv\n\n class _CalcipyConfig(CalcipyConfig):\n\n gto: GlobalTaskOptions = _gto\n\n if module and collection:\n raise ValueError('Only one of collection or module can be specified')\n\n _CalcipyProgram(\n name=pkg_name,\n version=pkg_version,\n namespace=Collection.from_module(module) if module else collection,\n config_class=_CalcipyConfig,\n ).run()\n"},{"location":"reference/calcipy/cli/#calcipy.cli.task","title":"task","text":"task(*dec_args, **dec_kwargs)\n Mark wrapped callable object as a valid Invoke task. Source code in calcipy/cli.py def task(*dec_args: Any, **dec_kwargs: Any) -> Callable: # type: ignore[type-arg]\n \"\"\"Mark wrapped callable object as a valid Invoke task.\"\"\"\n def wrapper(func: Any) -> Callable: # type: ignore[type-arg]\n # Attach arguments for Task\n setattr(func, TASK_ARGS_ATTR, dec_args)\n setattr(func, TASK_KWARGS_ATTR, dec_kwargs)\n # Attach public attributes from invoke that are expected\n func.help = dec_kwargs.pop('help', {})\n\n def _with_kwargs(**extra_kwargs: Any) -> Callable: # type: ignore[type-arg] # nosem\n \"\"\"Support building partial tasks.\"\"\"\n if extra_kwargs:\n # Set a unique name when 'extra_kwargs' was provided\n # https://github.com/pyinvoke/invoke/blob/07b836f2663bb073a7bcef3d6c454e1dc6b867ae/invoke/tasks.py#L81-L104\n encoded = b64encode(str(extra_kwargs).encode())\n func.__name__ = f'{func.__name__}_{encoded.decode().rstrip(\"=\")}'\n\n @wraps(func) # nosem\n def _with_kwargs_inner(*args: Any, **kwargs: Any) -> Any:\n return func(*args, **kwargs, **extra_kwargs)\n return _with_kwargs_inner\n\n func.with_kwargs = _with_kwargs\n\n @wraps(func) # nosem\n def _inner(*args: Any, **kwargs: Any) -> Any:\n return func(*args, **kwargs)\n\n return _inner\n\n # Handle the case when the decorator is called without arguments\n if (\n len(dec_args) == 1\n and callable(dec_args[0])\n and not hasattr(dec_args[0], TASK_ARGS_ATTR)\n ):\n return wrapper(dec_args[0])\n\n return wrapper\n"},{"location":"reference/calcipy/file_search/","title":"file_search","text":"Find Files."},{"location":"reference/calcipy/file_search/#calcipy.file_search-functions","title":"Functions","text":""},{"location":"reference/calcipy/file_search/#calcipy.file_search.find_project_files","title":"find_project_files","text":"find_project_files(path_project, ignore_patterns)\n Find project files in git version control. Note: uses the relative project directory and verifies that each file exists path_project: Path to the project directory\nignore_patterns: glob ignore patterns\n Dict[str, List[Path]]: where keys are the suffix (without leading dot) and values the list of paths\n Source code in calcipy/file_search.py @beartype\ndef find_project_files(path_project: Path, ignore_patterns: List[str]) -> List[Path]:\n \"\"\"Find project files in git version control.\n\n > Note: uses the relative project directory and verifies that each file exists\n\n Args:\n ----\n path_project: Path to the project directory\n ignore_patterns: glob ignore patterns\n\n Returns:\n -------\n Dict[str, List[Path]]: where keys are the suffix (without leading dot) and values the list of paths\n\n \"\"\"\n file_paths = []\n rel_filepaths = _get_all_files(cwd=path_project)\n filtered_rel_files = _filter_files(rel_filepaths=rel_filepaths, ignore_patterns=ignore_patterns)\n for rel_file in filtered_rel_files:\n path_file = path_project / rel_file\n if path_file.is_file():\n file_paths.append(path_file)\n else: # pragma: no cover\n LOGGER.warning('Could not find the specified file', path_file=path_file)\n return file_paths\n"},{"location":"reference/calcipy/file_search/#calcipy.file_search.find_project_files_by_suffix","title":"find_project_files_by_suffix","text":"find_project_files_by_suffix(path_project, *, ignore_patterns=None)\n Find project files in git version control. Note: uses the relative project directory and verifies that each file exists path_project: Path to the project directory\nignore_patterns: glob ignore patterns\n Dict[str, List[Path]]: where keys are the suffix (without leading dot) and values the list of paths\n Source code in calcipy/file_search.py @beartype\ndef find_project_files_by_suffix(\n path_project: Path, *, ignore_patterns: Optional[List[str]] = None,\n) -> Dict[str, List[Path]]:\n \"\"\"Find project files in git version control.\n\n > Note: uses the relative project directory and verifies that each file exists\n\n Args:\n ----\n path_project: Path to the project directory\n ignore_patterns: glob ignore patterns\n\n Returns:\n -------\n Dict[str, List[Path]]: where keys are the suffix (without leading dot) and values the list of paths\n\n \"\"\"\n file_lookup: Dict[str, List[Path]] = defaultdict(list)\n for path_file in find_project_files(path_project, ignore_patterns or []):\n file_lookup[path_file.suffix.lstrip('.')].append(path_file)\n return dict(file_lookup)\n"},{"location":"reference/calcipy/invoke_helpers/","title":"invoke_helpers","text":"Invoke Helpers."},{"location":"reference/calcipy/invoke_helpers/#calcipy.invoke_helpers-functions","title":"Functions","text":""},{"location":"reference/calcipy/invoke_helpers/#calcipy.invoke_helpers.get_doc_subdir","title":"get_doc_subdir","text":"get_doc_subdir(path_project=None)\n Retrieve the documentation directory from the copier answer file. path_project: Path to the project directory with contains `.copier-answers.yml`\n Path: to the source documentation directory\n Source code in calcipy/invoke_helpers.py @beartype\ndef get_doc_subdir(path_project: Optional[Path] = None) -> Path:\n \"\"\"Retrieve the documentation directory from the copier answer file.\n\n Args:\n ----\n path_project: Path to the project directory with contains `.copier-answers.yml`\n\n Returns:\n -------\n Path: to the source documentation directory\n\n \"\"\"\n path_copier = (path_project or get_project_path()) / COPIER_ANSWERS\n doc_dir = read_yaml_file(path_copier).get('doc_dir', 'docs')\n return path_copier.parent / doc_dir / 'docs'\n"},{"location":"reference/calcipy/invoke_helpers/#calcipy.invoke_helpers.get_project_path","title":"get_project_path cached","text":"get_project_path()\n Retrieve the cwd. Source code in calcipy/invoke_helpers.py @lru_cache(maxsize=1)\ndef get_project_path() -> Path:\n \"\"\"Retrieve the `cwd`.\"\"\"\n return Path.cwd()\n"},{"location":"reference/calcipy/invoke_helpers/#calcipy.invoke_helpers.run","title":"run","text":"run(ctx, *run_args, **run_kwargs)\n Wrap invoke.run to run within the working_dir. Source code in calcipy/invoke_helpers.py @beartype\ndef run(ctx: Context, *run_args: Any, **run_kwargs: Any) -> Optional[Result]:\n \"\"\"Wrap invoke.run to run within the `working_dir`.\"\"\"\n working_dir = '.'\n with suppress(AttributeError):\n working_dir = ctx.config.gto.working_dir\n\n with ctx.cd(working_dir):\n return ctx.run(*run_args, **run_kwargs)\n"},{"location":"reference/calcipy/invoke_helpers/#calcipy.invoke_helpers.use_pty","title":"use_pty cached","text":"use_pty()\n Return False on Windows and some CI environments. Source code in calcipy/invoke_helpers.py @lru_cache(maxsize=1)\ndef use_pty() -> bool:\n \"\"\"Return False on Windows and some CI environments.\"\"\"\n if platform.system() == 'Windows':\n return False\n return not environ.get('GITHUB_ACTION')\n"},{"location":"reference/calcipy/scripts/","title":"scripts","text":"Start the command line program."},{"location":"reference/calcipy/scripts/#calcipy.scripts-classes","title":"Classes","text":""},{"location":"reference/calcipy/scripts/#calcipy.scripts-functions","title":"Functions","text":""},{"location":"reference/calcipy/scripts/#calcipy.scripts.start","title":"start","text":"start()\n Run the customized Invoke Program. Source code in calcipy/scripts.py def start() -> None: # pragma: no cover\n \"\"\"Run the customized Invoke Program.\"\"\"\n from .tasks import all_tasks # noqa: PLC0415\n start_program(__pkg_name__, __version__, all_tasks)\n"},{"location":"reference/calcipy/scripts/#calcipy.scripts.start_docs","title":"start_docs","text":"start_docs()\n Run CLI with only the cl and doc namespaces. Source code in calcipy/scripts.py def start_docs() -> None: # pragma: no cover\n \"\"\"Run CLI with only the cl and doc namespaces.\"\"\"\n from .tasks import cl, doc # noqa: PLC0415\n _start_subset([cl, doc])\n"},{"location":"reference/calcipy/scripts/#calcipy.scripts.start_lint","title":"start_lint","text":"start_lint()\n Run CLI with only the lint namespace. Source code in calcipy/scripts.py def start_lint() -> None: # pragma: no cover\n \"\"\"Run CLI with only the lint namespace.\"\"\"\n from .tasks import lint # noqa: PLC0415\n _start_subset([lint])\n"},{"location":"reference/calcipy/scripts/#calcipy.scripts.start_pack","title":"start_pack","text":"start_pack()\n Run CLI with only the pack namespace. Source code in calcipy/scripts.py def start_pack() -> None: # pragma: no cover\n \"\"\"Run CLI with only the pack namespace.\"\"\"\n from .tasks import pack # noqa: PLC0415\n _start_subset([pack])\n"},{"location":"reference/calcipy/scripts/#calcipy.scripts.start_tags","title":"start_tags","text":"start_tags()\n Run CLI with only the tags namespace. Source code in calcipy/scripts.py def start_tags() -> None: # pragma: no cover\n \"\"\"Run CLI with only the tags namespace.\"\"\"\n from .tasks import tags # noqa: PLC0415\n _start_subset([tags])\n"},{"location":"reference/calcipy/scripts/#calcipy.scripts.start_test","title":"start_test","text":"start_test()\n Run CLI with only the test namespace. Source code in calcipy/scripts.py def start_test() -> None: # pragma: no cover\n \"\"\"Run CLI with only the test namespace.\"\"\"\n from .tasks import test # noqa: PLC0415\n _start_subset([test])\n"},{"location":"reference/calcipy/scripts/#calcipy.scripts.start_types","title":"start_types","text":"start_types()\n Run CLI with only the types namespace. Source code in calcipy/scripts.py def start_types() -> None: # pragma: no cover\n \"\"\"Run CLI with only the types namespace.\"\"\"\n from .tasks import types # noqa: PLC0415\n _start_subset([types])\n"},{"location":"reference/calcipy/check_for_stale_packages/","title":"check_for_stale_packages","text":""},{"location":"reference/calcipy/check_for_stale_packages/#calcipy.check_for_stale_packages-functions","title":"Functions","text":""},{"location":"reference/calcipy/check_for_stale_packages/#calcipy.check_for_stale_packages.check_for_stale_packages","title":"check_for_stale_packages","text":"check_for_stale_packages(*, stale_months, path_lock=LOCK, path_cache=CALCIPY_CACHE)\n Check for stale packages by reading from the cache, and updating if necessary. stale_months: cutoff in months for when a package might be stale enough to be a risk\npath_lock: path to poetry lock file\npath_cache: path to calcipy package cache file\n Source code in calcipy/check_for_stale_packages/_check_for_stale_packages.py @beartype\ndef check_for_stale_packages(*, stale_months: int, path_lock: Path = LOCK, path_cache: Path = CALCIPY_CACHE) -> bool:\n \"\"\"Check for stale packages by reading from the cache, and updating if necessary.\n\n Args:\n ----\n stale_months: cutoff in months for when a package might be stale enough to be a risk\n path_lock: path to poetry lock file\n path_cache: path to calcipy package cache file\n\n \"\"\"\n packages = _read_packages(path_lock)\n cached_packages = _read_cache(path_cache)\n if cached_packages and can_skip.can_skip(prerequisites=[path_lock], targets=[path_cache]):\n packages = [*cached_packages.values()]\n else:\n packages = _collect_release_dates(packages, cached_packages)\n _write_cache(packages, path_cache)\n return _packages_are_stale(packages, stale_months=stale_months)\n"},{"location":"reference/calcipy/check_for_stale_packages/_check_for_stale_packages/","title":"_check_for_stale_packages","text":"Check for stale packages."},{"location":"reference/calcipy/check_for_stale_packages/_check_for_stale_packages/#calcipy.check_for_stale_packages._check_for_stale_packages-attributes","title":"Attributes","text":""},{"location":"reference/calcipy/check_for_stale_packages/_check_for_stale_packages/#calcipy.check_for_stale_packages._check_for_stale_packages.CALCIPY_CACHE","title":"CALCIPY_CACHE module-attribute","text":"CALCIPY_CACHE = Path('.calcipy_packaging.lock')\n Path to the packaging lock file."},{"location":"reference/calcipy/check_for_stale_packages/_check_for_stale_packages/#calcipy.check_for_stale_packages._check_for_stale_packages-functions","title":"Functions","text":""},{"location":"reference/calcipy/check_for_stale_packages/_check_for_stale_packages/#calcipy.check_for_stale_packages._check_for_stale_packages.check_for_stale_packages","title":"check_for_stale_packages","text":"check_for_stale_packages(*, stale_months, path_lock=LOCK, path_cache=CALCIPY_CACHE)\n Check for stale packages by reading from the cache, and updating if necessary. stale_months: cutoff in months for when a package might be stale enough to be a risk\npath_lock: path to poetry lock file\npath_cache: path to calcipy package cache file\n Source code in calcipy/check_for_stale_packages/_check_for_stale_packages.py @beartype\ndef check_for_stale_packages(*, stale_months: int, path_lock: Path = LOCK, path_cache: Path = CALCIPY_CACHE) -> bool:\n \"\"\"Check for stale packages by reading from the cache, and updating if necessary.\n\n Args:\n ----\n stale_months: cutoff in months for when a package might be stale enough to be a risk\n path_lock: path to poetry lock file\n path_cache: path to calcipy package cache file\n\n \"\"\"\n packages = _read_packages(path_lock)\n cached_packages = _read_cache(path_cache)\n if cached_packages and can_skip.can_skip(prerequisites=[path_lock], targets=[path_cache]):\n packages = [*cached_packages.values()]\n else:\n packages = _collect_release_dates(packages, cached_packages)\n _write_cache(packages, path_cache)\n return _packages_are_stale(packages, stale_months=stale_months)\n"},{"location":"reference/calcipy/code_tag_collector/","title":"code_tag_collector","text":""},{"location":"reference/calcipy/code_tag_collector/#calcipy.code_tag_collector-functions","title":"Functions","text":""},{"location":"reference/calcipy/code_tag_collector/#calcipy.code_tag_collector.write_code_tag_file","title":"write_code_tag_file","text":"write_code_tag_file(\n *,\n path_tag_summary,\n paths_source,\n base_dir,\n regex=\"\",\n tags=\"\",\n header=\"# Task Summary\\n\\nAuto-Generated by `calcipy`\"\n)\n Create the code tag summary file. path_tag_summary: Path to the output file\npaths_source: list of source files to parse\nbase_dir: base directory relative to the searched files\nregex: compiled regular expression. Expected to have matching groups `(tag, text)`.\n Default is CODE_TAG_RE with tags from tag_order\ntags: subset of all tags to include in the report and specified order. Default is COMMON_CODE_TAGS\nheader: header text\n Source code in calcipy/code_tag_collector/_collector.py @beartype\ndef write_code_tag_file(\n *,\n path_tag_summary: Path,\n paths_source: List[Path],\n base_dir: Path,\n regex: str = '',\n tags: str = '',\n header: str = '# Task Summary\\n\\nAuto-Generated by `calcipy`',\n) -> None:\n \"\"\"Create the code tag summary file.\n\n Args:\n ----\n path_tag_summary: Path to the output file\n paths_source: list of source files to parse\n base_dir: base directory relative to the searched files\n regex: compiled regular expression. Expected to have matching groups `(tag, text)`.\n Default is CODE_TAG_RE with tags from tag_order\n tags: subset of all tags to include in the report and specified order. Default is COMMON_CODE_TAGS\n header: header text\n\n \"\"\"\n tag_order = [_t.strip() for _t in tags.split(',') if _t] or COMMON_CODE_TAGS\n matcher = (regex or CODE_TAG_RE).format(tag='|'.join(tag_order))\n\n matches = _search_files(paths_source, re.compile(matcher))\n if report := _format_report(\n base_dir, matches, tag_order=tag_order,\n ).strip():\n path_tag_summary.parent.mkdir(exist_ok=True, parents=True)\n path_tag_summary.write_text(f'{header}\\n\\n{report}\\n\\n<!-- {SKIP_PHRASE} -->\\n')\n LOGGER.text('Created Code Tag Summary', path_tag_summary=path_tag_summary)\n elif path_tag_summary.is_file():\n path_tag_summary.unlink()\n"},{"location":"reference/calcipy/code_tag_collector/_collector/","title":"_collector","text":"Collect code tags and output for review in a single location."},{"location":"reference/calcipy/code_tag_collector/_collector/#calcipy.code_tag_collector._collector-attributes","title":"Attributes","text":""},{"location":"reference/calcipy/code_tag_collector/_collector/#calcipy.code_tag_collector._collector.CODE_TAG_RE","title":"CODE_TAG_RE module-attribute","text":"CODE_TAG_RE = '((^|\\\\s|\\\\(|\"|\\\\\\')(?P<tag>{tag})(:| -)([^\\\\r\\\\n]))(?P<text>.+)'\n Default code tag regex with tag and text matching groups. Requires formatting with list of tags: CODE_TAG_RE.format(tag='|'.join(tag_list)) Commonly, the tag_list could be COMMON_CODE_TAGS"},{"location":"reference/calcipy/code_tag_collector/_collector/#calcipy.code_tag_collector._collector.COMMON_CODE_TAGS","title":"COMMON_CODE_TAGS module-attribute","text":"COMMON_CODE_TAGS = ['FIXME', 'TODO', 'PLANNED', 'HACK', 'REVIEW', 'TBD', 'DEBUG']\n Most common code tags. FYI and NOTE are excluded to not be tracked in the Code Summary."},{"location":"reference/calcipy/code_tag_collector/_collector/#calcipy.code_tag_collector._collector.SKIP_PHRASE","title":"SKIP_PHRASE module-attribute","text":"SKIP_PHRASE = 'calcipy_skip_tags'\n String that indicates the file should be excluded from the tag search."},{"location":"reference/calcipy/code_tag_collector/_collector/#calcipy.code_tag_collector._collector-functions","title":"Functions","text":""},{"location":"reference/calcipy/code_tag_collector/_collector/#calcipy.code_tag_collector._collector.github_blame_url","title":"github_blame_url","text":"github_blame_url(clone_uri)\n Format the blame URL. clone_uri: git remote URI\n str: repo_url Source code in calcipy/code_tag_collector/_collector.py @beartype\ndef github_blame_url(clone_uri: str) -> str:\n \"\"\"Format the blame URL.\n\n Args:\n ----\n clone_uri: git remote URI\n\n Returns:\n -------\n str: `repo_url`\n\n \"\"\"\n # Could be ssh or http (with or without .git)\n # > git@github.com:KyleKing/calcipy.git\n # > https://github.com/KyleKing/calcipy.git\n if matches := re.compile(_GITHUB_ORIGIN).match(clone_uri):\n github_url = 'https://github.com/'\n return f\"{github_url}{matches['owner']}/{matches['repository']}\"\n return ''\n"},{"location":"reference/calcipy/code_tag_collector/_collector/#calcipy.code_tag_collector._collector.write_code_tag_file","title":"write_code_tag_file","text":"write_code_tag_file(\n *,\n path_tag_summary,\n paths_source,\n base_dir,\n regex=\"\",\n tags=\"\",\n header=\"# Task Summary\\n\\nAuto-Generated by `calcipy`\"\n)\n Create the code tag summary file. path_tag_summary: Path to the output file\npaths_source: list of source files to parse\nbase_dir: base directory relative to the searched files\nregex: compiled regular expression. Expected to have matching groups `(tag, text)`.\n Default is CODE_TAG_RE with tags from tag_order\ntags: subset of all tags to include in the report and specified order. Default is COMMON_CODE_TAGS\nheader: header text\n Source code in calcipy/code_tag_collector/_collector.py @beartype\ndef write_code_tag_file(\n *,\n path_tag_summary: Path,\n paths_source: List[Path],\n base_dir: Path,\n regex: str = '',\n tags: str = '',\n header: str = '# Task Summary\\n\\nAuto-Generated by `calcipy`',\n) -> None:\n \"\"\"Create the code tag summary file.\n\n Args:\n ----\n path_tag_summary: Path to the output file\n paths_source: list of source files to parse\n base_dir: base directory relative to the searched files\n regex: compiled regular expression. Expected to have matching groups `(tag, text)`.\n Default is CODE_TAG_RE with tags from tag_order\n tags: subset of all tags to include in the report and specified order. Default is COMMON_CODE_TAGS\n header: header text\n\n \"\"\"\n tag_order = [_t.strip() for _t in tags.split(',') if _t] or COMMON_CODE_TAGS\n matcher = (regex or CODE_TAG_RE).format(tag='|'.join(tag_order))\n\n matches = _search_files(paths_source, re.compile(matcher))\n if report := _format_report(\n base_dir, matches, tag_order=tag_order,\n ).strip():\n path_tag_summary.parent.mkdir(exist_ok=True, parents=True)\n path_tag_summary.write_text(f'{header}\\n\\n{report}\\n\\n<!-- {SKIP_PHRASE} -->\\n')\n LOGGER.text('Created Code Tag Summary', path_tag_summary=path_tag_summary)\n elif path_tag_summary.is_file():\n path_tag_summary.unlink()\n"},{"location":"reference/calcipy/dot_dict/","title":"dot_dict","text":""},{"location":"reference/calcipy/dot_dict/#calcipy.dot_dict-functions","title":"Functions","text":""},{"location":"reference/calcipy/dot_dict/#calcipy.dot_dict.ddict","title":"ddict","text":"ddict(**kwargs)\n Return a dotted dictionary that can also be accessed normally. Currently uses python-box Could consider cleverdict which had updates as recently as 2022 There are numerous other variations that haven\u2019t been updated since 2020, such as munch, bunch, ddict **kwargs: keyword arguments formatted into dictionary\n DdictType: dotted dictionary\n Source code in calcipy/dot_dict/_dot_dict.py @beartype\ndef ddict(**kwargs: Dict[str, Any]) -> DdictType:\n \"\"\"Return a dotted dictionary that can also be accessed normally.\n\n - Currently uses `python-box`\n - Could consider `cleverdict` which had updates as recently as 2022\n - There are numerous other variations that haven't been updated since 2020, such as `munch`, `bunch`, `ddict`\n\n Args:\n ----\n **kwargs: keyword arguments formatted into dictionary\n\n Returns:\n -------\n DdictType: dotted dictionary\n\n \"\"\"\n return Box(kwargs)\n"},{"location":"reference/calcipy/dot_dict/_dot_dict/","title":"_dot_dict","text":"Dotted dictionary for consistent interface. Consider moving to Corallium, but I don\u2019t have any uses for it yet."},{"location":"reference/calcipy/dot_dict/_dot_dict/#calcipy.dot_dict._dot_dict-attributes","title":"Attributes","text":""},{"location":"reference/calcipy/dot_dict/_dot_dict/#calcipy.dot_dict._dot_dict.DdictType","title":"DdictType module-attribute","text":"DdictType = Union[Dict[str, Any], Box]\n Return type from ddict()."},{"location":"reference/calcipy/dot_dict/_dot_dict/#calcipy.dot_dict._dot_dict-functions","title":"Functions","text":""},{"location":"reference/calcipy/dot_dict/_dot_dict/#calcipy.dot_dict._dot_dict.ddict","title":"ddict","text":"ddict(**kwargs)\n Return a dotted dictionary that can also be accessed normally. Currently uses python-box Could consider cleverdict which had updates as recently as 2022 There are numerous other variations that haven\u2019t been updated since 2020, such as munch, bunch, ddict **kwargs: keyword arguments formatted into dictionary\n DdictType: dotted dictionary\n Source code in calcipy/dot_dict/_dot_dict.py @beartype\ndef ddict(**kwargs: Dict[str, Any]) -> DdictType:\n \"\"\"Return a dotted dictionary that can also be accessed normally.\n\n - Currently uses `python-box`\n - Could consider `cleverdict` which had updates as recently as 2022\n - There are numerous other variations that haven't been updated since 2020, such as `munch`, `bunch`, `ddict`\n\n Args:\n ----\n **kwargs: keyword arguments formatted into dictionary\n\n Returns:\n -------\n DdictType: dotted dictionary\n\n \"\"\"\n return Box(kwargs)\n"},{"location":"reference/calcipy/experiments/","title":"experiments","text":""},{"location":"reference/calcipy/experiments/bump_programmatically/","title":"bump_programmatically","text":"Experiment with bumping the git tag using griffe."},{"location":"reference/calcipy/experiments/bump_programmatically/#calcipy.experiments.bump_programmatically-functions","title":"Functions","text":""},{"location":"reference/calcipy/experiments/bump_programmatically/#calcipy.experiments.bump_programmatically.bump_tag","title":"bump_tag","text":"bump_tag(*, pkg_name, tag, tag_prefix)\n Make a SemVer minor bump using griffe if there were any breaking changes. Major versions must be bumped manually Source code in calcipy/experiments/bump_programmatically.py @beartype\ndef bump_tag(*, pkg_name: str, tag: str, tag_prefix: str) -> str:\n \"\"\"Make a SemVer minor bump using `griffe` if there were any breaking changes.\n\n Major versions must be bumped manually\n\n \"\"\"\n previous = griffe.load_git(pkg_name, ref=tag)\n current = griffe.load(pkg_name)\n\n breakages = [*griffe.find_breaking_changes(previous, current)]\n for breakage in breakages:\n try:\n LOGGER.text(breakage._explain_oneline()) # noqa: SLF001\n except BuiltinModuleError: # noqa: PERF203\n LOGGER.warning(str(breakage))\n except Exception:\n LOGGER.exception(str(breakage))\n\n try:\n ver = semver.Version.parse(tag.replace(tag_prefix, ''))\n except ValueError:\n LOGGER.exception('Failed to parse tag', tag=tag)\n return ''\n new_ver = ver.bump_minor() if any(breakages) else ver.bump_patch()\n return f'{tag_prefix}{new_ver}'\n"},{"location":"reference/calcipy/experiments/check_duplicate_test_names/","title":"check_duplicate_test_names","text":"Experiment with checking for duplicate test names."},{"location":"reference/calcipy/experiments/check_duplicate_test_names/#calcipy.experiments.check_duplicate_test_names-functions","title":"Functions","text":""},{"location":"reference/calcipy/experiments/check_duplicate_test_names/#calcipy.experiments.check_duplicate_test_names.run","title":"run","text":"run(test_path)\n Check for duplicates in the test suite. Inspired by: https://stackoverflow.com/a/67840804/3219667 Source code in calcipy/experiments/check_duplicate_test_names.py @beartype\ndef run(test_path: Path) -> List[str]: # noqa: C901 # pylint: disable=too-complex\n \"\"\"Check for duplicates in the test suite.\n\n Inspired by: https://stackoverflow.com/a/67840804/3219667\n\n \"\"\"\n summary = set()\n duplicates = []\n\n for path_test in test_path.rglob('test_*.py'): # pylint: disable=too-many-nested-blocks\n LOGGER.info(path_test.as_posix())\n parsed_ast = ast.parse(path_test.read_text())\n\n for node in parsed_ast.body:\n if isinstance(node, ast.FunctionDef):\n if node.name in summary and node.name.startswith('test_'):\n duplicates.append(node.name)\n summary.add(node.name)\n _show_info(node)\n elif isinstance(node, ast.ClassDef):\n LOGGER.info('Class name', name=node.name)\n for method in node.body:\n if isinstance(method, ast.FunctionDef):\n _show_info(method)\n\n for node in ast.walk(parsed_ast): # type: ignore[assignment]\n if (\n isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))\n and node.name not in summary\n ):\n LOGGER.info('Found new function(s) through walking')\n _show_info(node)\n summary.add(node.name)\n\n if duplicates:\n LOGGER.error('Found Duplicates', duplicates=duplicates)\n return duplicates\n"},{"location":"reference/calcipy/md_writer/","title":"md_writer","text":""},{"location":"reference/calcipy/md_writer/#calcipy.md_writer-functions","title":"Functions","text":""},{"location":"reference/calcipy/md_writer/#calcipy.md_writer.write_autoformatted_md_sections","title":"write_autoformatted_md_sections","text":"write_autoformatted_md_sections(handler_lookup=None, paths_md=None)\n Populate the auto-formatted sections of markdown files with user-configured logic. Source code in calcipy/md_writer/_writer.py @beartype\ndef write_autoformatted_md_sections(\n handler_lookup: Optional[HandlerLookupT] = None,\n paths_md: Optional[List[Path]] = None,\n) -> None:\n \"\"\"Populate the auto-formatted sections of markdown files with user-configured logic.\"\"\"\n _lookup: HandlerLookupT = handler_lookup or {\n 'COVERAGE ': _handle_coverage,\n 'SOURCE_FILE=': _handle_source_file,\n }\n\n paths = paths_md or find_project_files_by_suffix(get_project_path()).get('md') or []\n for path_md in paths:\n LOGGER.text_debug('Processing', path_md=path_md)\n if md_lines := _ReplacementMachine().parse(read_lines(path_md), _lookup, path_md):\n path_md.write_text('\\n'.join(md_lines) + '\\n', encoding='utf-8')\n"},{"location":"reference/calcipy/md_writer/_writer/","title":"_writer","text":"Markdown Machine."},{"location":"reference/calcipy/md_writer/_writer/#calcipy.md_writer._writer-attributes","title":"Attributes","text":""},{"location":"reference/calcipy/md_writer/_writer/#calcipy.md_writer._writer.HandlerLookupT","title":"HandlerLookupT module-attribute","text":"HandlerLookupT = Dict[str, Callable[[str, Path], List[str]]]\n Handler Lookup."},{"location":"reference/calcipy/md_writer/_writer/#calcipy.md_writer._writer-functions","title":"Functions","text":""},{"location":"reference/calcipy/md_writer/_writer/#calcipy.md_writer._writer.write_autoformatted_md_sections","title":"write_autoformatted_md_sections","text":"write_autoformatted_md_sections(handler_lookup=None, paths_md=None)\n Populate the auto-formatted sections of markdown files with user-configured logic. Source code in calcipy/md_writer/_writer.py @beartype\ndef write_autoformatted_md_sections(\n handler_lookup: Optional[HandlerLookupT] = None,\n paths_md: Optional[List[Path]] = None,\n) -> None:\n \"\"\"Populate the auto-formatted sections of markdown files with user-configured logic.\"\"\"\n _lookup: HandlerLookupT = handler_lookup or {\n 'COVERAGE ': _handle_coverage,\n 'SOURCE_FILE=': _handle_source_file,\n }\n\n paths = paths_md or find_project_files_by_suffix(get_project_path()).get('md') or []\n for path_md in paths:\n LOGGER.text_debug('Processing', path_md=path_md)\n if md_lines := _ReplacementMachine().parse(read_lines(path_md), _lookup, path_md):\n path_md.write_text('\\n'.join(md_lines) + '\\n', encoding='utf-8')\n"},{"location":"reference/calcipy/noxfile/","title":"noxfile","text":""},{"location":"reference/calcipy/noxfile/#calcipy.noxfile-functions","title":"Functions","text":""},{"location":"reference/calcipy/noxfile/#calcipy.noxfile.build_check","title":"build_check","text":"build_check(session)\n Check that the built output meets all checks. Source code in calcipy/noxfile/_noxfile.py @nox_poetry_session(python=_get_pythons()[-1:], reuse_venv=True)\ndef build_check(session: NPSession) -> None: # pragma: no cover\n \"\"\"Check that the built output meets all checks.\"\"\"\n # Build sdist and fix return URI, which will have file://...#egg=calcipy\n sdist_uri = session.poetry.build_package(distribution_format=DistributionFormat.SDIST)\n path_sdist = Path(url2pathname(urlparse(sdist_uri).path))\n LOGGER.text_debug('Fixed sdist URI', sdist_uri=sdist_uri, path_sdist=path_sdist)\n # Check with pyroma\n session.install('pyroma>=4.0', '--upgrade')\n # required for \"poetry.core.masonry.api\" build backend\n session.run('python', '-m', 'pip', 'install', 'poetry>=1.3', stdout=True)\n session.run('pyroma', '--file', path_sdist.as_posix(), '--min=9', stdout=True)\n"},{"location":"reference/calcipy/noxfile/#calcipy.noxfile.build_dist","title":"build_dist","text":"build_dist(session)\n Build and test the project files within a controlled environment for repeatability. Source code in calcipy/noxfile/_noxfile.py @nox_session(python=_get_pythons()[-1:], reuse_venv=False)\ndef build_dist(session: Union[NoxSession, NPSession]) -> None: # pragma: no cover\n \"\"\"Build and test the project files within a controlled environment for repeatability.\"\"\"\n dist_path = Path('dist')\n if_found_unlink(dist_path)\n\n # Support 'corallium' by re-implementing \"session.poetry.build_package()\", from:\n # https://github.com/cjolowicz/nox-poetry/blob/5772b66ebff8d5a3351a08ed402d3d31e48be5f8/src/nox_poetry/sessions.py#L233-L255\n # https://github.com/cjolowicz/nox-poetry/blob/5772b66ebff8d5a3351a08ed402d3d31e48be5f8/src/nox_poetry/poetry.py#L111-L154\n output = session.run(*shlex.split('poetry build --format=wheel --no-ansi'),\n external=True, silent=True)\n output = cast(str, output)\n wheel = dist_path / output.split()[-1]\n path_wheel = wheel.resolve().as_uri()\n\n LOGGER.text('Created wheel', path_wheel=path_wheel)\n # Install the wheel and check that imports without any of the optional dependencies\n session.install(path_wheel)\n session.run(*shlex.split('python scripts/check_imports.py'), stdout=True)\n"},{"location":"reference/calcipy/noxfile/#calcipy.noxfile.tests","title":"tests","text":"tests(session)\n Run doit test task for specified python versions. Source code in calcipy/noxfile/_noxfile.py @nox_session(python=_get_pythons(), reuse_venv=True)\ndef tests(session: Union[NoxSession, NPSession]) -> None: # pragma: no cover\n \"\"\"Run doit test task for specified python versions.\"\"\"\n _install_local(session, ['ddict', 'doc', 'lint', 'nox', 'stale', 'tags', 'test'])\n session.run(*shlex.split('pytest ./tests'), stdout=True, env={'RUNTIME_TYPE_CHECKING_MODE': 'WARNING'})\n"},{"location":"reference/calcipy/noxfile/_noxfile/","title":"_noxfile","text":"nox-poetry configuration file. Useful snippets from docs poetry run nox -l\npoetry run nox --list-sessions\n\npoetry run nox -s build_check-3.8 build_dist-3.8 tests-3.8\npoetry run nox --session tests-3.11\n\npoetry run nox --python 3.8\n\npoetry run nox -k \"not build_check and not build_dist\"\n Useful nox snippets # Example conditionally skipping a session\nif not session.interactive:\n session.skip('Cannot run detect-secrets audit in non-interactive shell')\n\n# Install pinned version\nsession.install('detect-secrets==1.0.3')\n\n# Example capturing STDOUT into a file (could do the same for stderr)\npath_stdout = Path('.stdout.txt').resolve()\nwith open(path_stdout, 'w') as out:\n session.run(*shlex.split('echo Hello World!'), stdout=out)\n"},{"location":"reference/calcipy/noxfile/_noxfile/#calcipy.noxfile._noxfile-functions","title":"Functions","text":""},{"location":"reference/calcipy/noxfile/_noxfile/#calcipy.noxfile._noxfile.build_check","title":"build_check","text":"build_check(session)\n Check that the built output meets all checks. Source code in calcipy/noxfile/_noxfile.py @nox_poetry_session(python=_get_pythons()[-1:], reuse_venv=True)\ndef build_check(session: NPSession) -> None: # pragma: no cover\n \"\"\"Check that the built output meets all checks.\"\"\"\n # Build sdist and fix return URI, which will have file://...#egg=calcipy\n sdist_uri = session.poetry.build_package(distribution_format=DistributionFormat.SDIST)\n path_sdist = Path(url2pathname(urlparse(sdist_uri).path))\n LOGGER.text_debug('Fixed sdist URI', sdist_uri=sdist_uri, path_sdist=path_sdist)\n # Check with pyroma\n session.install('pyroma>=4.0', '--upgrade')\n # required for \"poetry.core.masonry.api\" build backend\n session.run('python', '-m', 'pip', 'install', 'poetry>=1.3', stdout=True)\n session.run('pyroma', '--file', path_sdist.as_posix(), '--min=9', stdout=True)\n"},{"location":"reference/calcipy/noxfile/_noxfile/#calcipy.noxfile._noxfile.build_dist","title":"build_dist","text":"build_dist(session)\n Build and test the project files within a controlled environment for repeatability. Source code in calcipy/noxfile/_noxfile.py @nox_session(python=_get_pythons()[-1:], reuse_venv=False)\ndef build_dist(session: Union[NoxSession, NPSession]) -> None: # pragma: no cover\n \"\"\"Build and test the project files within a controlled environment for repeatability.\"\"\"\n dist_path = Path('dist')\n if_found_unlink(dist_path)\n\n # Support 'corallium' by re-implementing \"session.poetry.build_package()\", from:\n # https://github.com/cjolowicz/nox-poetry/blob/5772b66ebff8d5a3351a08ed402d3d31e48be5f8/src/nox_poetry/sessions.py#L233-L255\n # https://github.com/cjolowicz/nox-poetry/blob/5772b66ebff8d5a3351a08ed402d3d31e48be5f8/src/nox_poetry/poetry.py#L111-L154\n output = session.run(*shlex.split('poetry build --format=wheel --no-ansi'),\n external=True, silent=True)\n output = cast(str, output)\n wheel = dist_path / output.split()[-1]\n path_wheel = wheel.resolve().as_uri()\n\n LOGGER.text('Created wheel', path_wheel=path_wheel)\n # Install the wheel and check that imports without any of the optional dependencies\n session.install(path_wheel)\n session.run(*shlex.split('python scripts/check_imports.py'), stdout=True)\n"},{"location":"reference/calcipy/noxfile/_noxfile/#calcipy.noxfile._noxfile.tests","title":"tests","text":"tests(session)\n Run doit test task for specified python versions. Source code in calcipy/noxfile/_noxfile.py @nox_session(python=_get_pythons(), reuse_venv=True)\ndef tests(session: Union[NoxSession, NPSession]) -> None: # pragma: no cover\n \"\"\"Run doit test task for specified python versions.\"\"\"\n _install_local(session, ['ddict', 'doc', 'lint', 'nox', 'stale', 'tags', 'test'])\n session.run(*shlex.split('pytest ./tests'), stdout=True, env={'RUNTIME_TYPE_CHECKING_MODE': 'WARNING'})\n"},{"location":"reference/calcipy/tasks/","title":"tasks","text":""},{"location":"reference/calcipy/tasks/_invoke/","title":"_invoke","text":"Extend Invoke for Calcipy."},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke-classes","title":"Classes","text":""},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke.Collection","title":"Collection","text":" Bases: Collection Source code in calcipy/tasks/_invoke.py class Collection(InvokeCollection):\n\n @classmethod\n def from_module(\n cls,\n module: ModuleType,\n name: Optional[str] = None,\n config: Optional[Dict[str, Any]] = None,\n loaded_from: Optional[str] = None,\n auto_dash_names: Optional[bool] = None,\n ) -> 'InvokeCollection':\n \"\"\"Extend search for a namespace, Task, or deferred task.\"\"\"\n collection = super().from_module(\n module=module,\n name=name,\n config=config,\n loaded_from=loaded_from,\n auto_dash_names=auto_dash_names,\n )\n\n # If tasks were not loaded from a namespace or otherwise found\n if not collection.task_names:\n # Look for any decorated, but deferred \"Tasks\"\n for task in (fxn for fxn in vars(module).values() if hasattr(fxn, TASK_ARGS_ATTR)):\n collection.add_task(task)\n\n return collection\n\n def add_task(\n self,\n task: DeferedTask,\n name: Optional[str] = None,\n aliases: Optional[Tuple[str, ...]] = None,\n default: Optional[bool] = None,\n ) -> None:\n \"\"\"Extend for deferred tasks.\"\"\"\n super().add_task(task=_build_task(task), name=name, aliases=aliases, default=default)\n"},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke.Collection-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke.Collection.add_task","title":"add_task","text":"add_task(task, name=None, aliases=None, default=None)\n Extend for deferred tasks. Source code in calcipy/tasks/_invoke.py def add_task(\n self,\n task: DeferedTask,\n name: Optional[str] = None,\n aliases: Optional[Tuple[str, ...]] = None,\n default: Optional[bool] = None,\n) -> None:\n \"\"\"Extend for deferred tasks.\"\"\"\n super().add_task(task=_build_task(task), name=name, aliases=aliases, default=default)\n"},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke.Collection.from_module","title":"from_module classmethod","text":"from_module(module, name=None, config=None, loaded_from=None, auto_dash_names=None)\n Extend search for a namespace, Task, or deferred task. Source code in calcipy/tasks/_invoke.py @classmethod\ndef from_module(\n cls,\n module: ModuleType,\n name: Optional[str] = None,\n config: Optional[Dict[str, Any]] = None,\n loaded_from: Optional[str] = None,\n auto_dash_names: Optional[bool] = None,\n) -> 'InvokeCollection':\n \"\"\"Extend search for a namespace, Task, or deferred task.\"\"\"\n collection = super().from_module(\n module=module,\n name=name,\n config=config,\n loaded_from=loaded_from,\n auto_dash_names=auto_dash_names,\n )\n\n # If tasks were not loaded from a namespace or otherwise found\n if not collection.task_names:\n # Look for any decorated, but deferred \"Tasks\"\n for task in (fxn for fxn in vars(module).values() if hasattr(fxn, TASK_ARGS_ATTR)):\n collection.add_task(task)\n\n return collection\n"},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke.GlobalTaskOptions","title":"GlobalTaskOptions","text":" Bases: BaseModel Global Task Options. Source code in calcipy/tasks/_invoke.py class GlobalTaskOptions(BaseModel):\n \"\"\"Global Task Options.\"\"\"\n\n working_dir: Path = Field(default_factory=Path.cwd)\n \"\"\"Working directory for the program to use globally.\"\"\"\n\n file_args: List[Path] = Field(default_factory=list)\n \"\"\"List of Paths to modify.\"\"\"\n\n verbose: PositiveInt = Field(default=0, le=3)\n \"\"\"Verbosity level.\"\"\"\n\n keep_going: bool = False\n \"\"\"Continue task execution regardless of failure.\"\"\"\n"},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke.GlobalTaskOptions-attributes","title":"Attributes","text":""},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke.GlobalTaskOptions.file_args","title":"file_args class-attribute instance-attribute","text":"file_args = Field(default_factory=list)\n List of Paths to modify."},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke.GlobalTaskOptions.keep_going","title":"keep_going class-attribute instance-attribute","text":"keep_going = False\n Continue task execution regardless of failure."},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke.GlobalTaskOptions.verbose","title":"verbose class-attribute instance-attribute","text":"verbose = Field(default=0, le=3)\n Verbosity level."},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke.GlobalTaskOptions.working_dir","title":"working_dir class-attribute instance-attribute","text":"working_dir = Field(default_factory=cwd)\n Working directory for the program to use globally."},{"location":"reference/calcipy/tasks/all_tasks/","title":"all_tasks","text":"Tasks can be imported piecemeal or imported in their entirety from here."},{"location":"reference/calcipy/tasks/all_tasks/#calcipy.tasks.all_tasks-attributes","title":"Attributes","text":""},{"location":"reference/calcipy/tasks/all_tasks/#calcipy.tasks.all_tasks.TaskList","title":"TaskList module-attribute","text":"TaskList = List[Union[Call, DeferedTask]]\n List of wrapped or normal task functions."},{"location":"reference/calcipy/tasks/all_tasks/#calcipy.tasks.all_tasks-classes","title":"Classes","text":""},{"location":"reference/calcipy/tasks/all_tasks/#calcipy.tasks.all_tasks-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/all_tasks/#calcipy.tasks.all_tasks.main","title":"main","text":"main(_ctx)\n Run main task pipeline. Source code in calcipy/tasks/all_tasks.py @task(post=with_progress(_MAIN_TASKS))\ndef main(_ctx: Context) -> None:\n \"\"\"Run main task pipeline.\"\"\"\n"},{"location":"reference/calcipy/tasks/all_tasks/#calcipy.tasks.all_tasks.other","title":"other","text":"other(_ctx)\n Run tasks that are otherwise not exercised in main. Source code in calcipy/tasks/all_tasks.py @task(post=with_progress(_OTHER_TASKS))\ndef other(_ctx: Context) -> None:\n \"\"\"Run tasks that are otherwise not exercised in main.\"\"\"\n"},{"location":"reference/calcipy/tasks/all_tasks/#calcipy.tasks.all_tasks.progress","title":"progress","text":"progress(_ctx, *, index, total)\n Progress Task. Source code in calcipy/tasks/all_tasks.py @task(\n help={\n 'index': 'Current index (0-indexed)',\n 'total': 'Total steps',\n },\n show_task_info=False,\n)\ndef progress(_ctx: Context, *, index: int, total: int) -> None:\n \"\"\"Progress Task.\"\"\"\n LOGGER.text('Progress', is_header=True, index=index + 1, total=total)\n"},{"location":"reference/calcipy/tasks/all_tasks/#calcipy.tasks.all_tasks.release","title":"release","text":"release(ctx, *, suffix=None)\n Run release pipeline. Source code in calcipy/tasks/all_tasks.py @task(\n help=cl.bump.help, # pyright: ignore[reportFunctionMemberAccess]\n post=with_progress(\n [\n pack.lock,\n doc.build,\n doc.deploy,\n pack.publish,\n ],\n offset=1,\n ),\n)\ndef release(ctx: Context, *, suffix: cl.SuffixT = None) -> None:\n \"\"\"Run release pipeline.\"\"\"\n cl.bumpz(ctx, suffix=suffix)\n"},{"location":"reference/calcipy/tasks/all_tasks/#calcipy.tasks.all_tasks.summary","title":"summary","text":"summary(_ctx, *, message)\n Summary Task. Source code in calcipy/tasks/all_tasks.py @task(\n help={\n 'message': 'String message to display',\n },\n show_task_info=False,\n)\ndef summary(_ctx: Context, *, message: str) -> None:\n \"\"\"Summary Task.\"\"\"\n LOGGER.text(message, is_header=True)\n"},{"location":"reference/calcipy/tasks/all_tasks/#calcipy.tasks.all_tasks.with_progress","title":"with_progress","text":"with_progress(items, offset=0)\n Inject intermediary \u2018progress\u2019 tasks. items: list of tasks\noffset: Optional offset to shift counters\n Source code in calcipy/tasks/all_tasks.py @beartype\ndef with_progress(items: Any, offset: int = 0) -> TaskList:\n \"\"\"Inject intermediary 'progress' tasks.\n\n Args:\n ----\n items: list of tasks\n offset: Optional offset to shift counters\n\n \"\"\"\n task_items = [_build_task(_t) for _t in items]\n message = 'Running tasks: ' + ', '.join([str(_t.__name__) for _t in task_items])\n tasks: TaskList = [summary.with_kwargs(message=message)] # pyright: ignore[reportFunctionMemberAccess]\n\n total = len(task_items) + offset\n for idx, item in enumerate(task_items):\n tasks += [\n progress.with_kwargs(index=idx + offset, total=total), # pyright: ignore[reportFunctionMemberAccess]\n item,\n ]\n return tasks\n"},{"location":"reference/calcipy/tasks/cl/","title":"cl","text":"Changelog CLI."},{"location":"reference/calcipy/tasks/cl/#calcipy.tasks.cl-attributes","title":"Attributes","text":""},{"location":"reference/calcipy/tasks/cl/#calcipy.tasks.cl.SuffixT","title":"SuffixT module-attribute","text":"SuffixT = Optional[Literal['alpha', 'beta', 'rc']]\n Prerelease Suffix Type."},{"location":"reference/calcipy/tasks/cl/#calcipy.tasks.cl-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/cl/#calcipy.tasks.cl.bump","title":"bump","text":"bump(ctx, *, suffix=None)\n Bumps project version based on commits & settings in pyproject.toml. Source code in calcipy/tasks/cl.py @task(\n pre=[write],\n help={\n 'suffix': 'Specify prerelease suffix for version bump (alpha, beta, rc)',\n },\n)\ndef bump(ctx: Context, *, suffix: SuffixT = None) -> None:\n \"\"\"Bumps project version based on commits & settings in pyproject.toml.\"\"\"\n bumpz(ctx, suffix=suffix)\n"},{"location":"reference/calcipy/tasks/cl/#calcipy.tasks.cl.bumpz","title":"bumpz","text":"bumpz(ctx, *, suffix=None)\n Bumps project version based on commits & settings in pyproject.toml. Source code in calcipy/tasks/cl.py @beartype\ndef bumpz(ctx: Context, *, suffix: SuffixT = None) -> None:\n \"\"\"Bumps project version based on commits & settings in pyproject.toml.\"\"\"\n check_installed(ctx, executable='gh', message=GH_MESSAGE)\n\n opt_cz_args = f' --prerelease={suffix}' if suffix else ''\n run(ctx, f'{python_dir()}/cz bump{opt_cz_args} --annotated-tag --no-verify --gpg-sign')\n\n run(ctx, 'git push origin --tags --no-verify')\n\n get_last_tag = 'git tag --list --sort=-creatordate | head -n 1'\n opt_gh_args = ' --prerelease' if suffix else ''\n run(ctx, f'gh release create --generate-notes $({get_last_tag}){opt_gh_args}')\n"},{"location":"reference/calcipy/tasks/cl/#calcipy.tasks.cl.write","title":"write","text":"write(ctx)\n Write a Changelog file with the raw Git history. Resources: https://keepachangelog.com/en/1.0.0/ https://www.conventionalcommits.org/en/v1.0.0/ https://writingfordevelopers.substack.com/p/how-to-write-a-commit-message https://chris.beams.io/posts/git-commit/ https://semver.org/ https://calver.org/ "},{"location":"reference/calcipy/tasks/cl/#calcipy.tasks.cl.write--returns","title":"Returns","text":"List[DoitAction]: doit actions\n Source code in calcipy/tasks/cl.py @task()\ndef write(ctx: Context) -> None:\n \"\"\"Write a Changelog file with the raw Git history.\n\n Resources:\n\n - https://keepachangelog.com/en/1.0.0/\n - https://www.conventionalcommits.org/en/v1.0.0/\n - https://writingfordevelopers.substack.com/p/how-to-write-a-commit-message\n - https://chris.beams.io/posts/git-commit/\n - https://semver.org/\n - https://calver.org/\n\n Returns\n -------\n List[DoitAction]: doit actions\n\n \"\"\"\n run(ctx, f'{python_dir()}/cz changelog')\n path_cl = get_project_path() / 'CHANGELOG.md'\n if not path_cl.is_file():\n msg = f'Could not locate the changelog at: {path_cl}'\n raise FileNotFoundError(msg)\n path_cl.replace(get_doc_subdir() / path_cl.name)\n"},{"location":"reference/calcipy/tasks/defaults/","title":"defaults","text":"Calcipy-Invoke Defaults."},{"location":"reference/calcipy/tasks/defaults/#calcipy.tasks.defaults-classes","title":"Classes","text":""},{"location":"reference/calcipy/tasks/defaults/#calcipy.tasks.defaults-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/defaults/#calcipy.tasks.defaults.from_ctx","title":"from_ctx","text":"from_ctx(ctx, group, key)\n Safely extract the value from the context or the defaults. Instead of ctx.tests.out_dir use from_ctx(ctx, 'test', 'out_dir') Source code in calcipy/tasks/defaults.py @beartype\ndef from_ctx(ctx: Context, group: str, key: str) -> str:\n \"\"\"Safely extract the value from the context or the defaults.\n\n Instead of `ctx.tests.out_dir` use `from_ctx(ctx, 'test', 'out_dir')`\n\n \"\"\"\n with suppress(KeyError):\n return str(ctx.config[group][key])\n return str(DEFAULTS[group][key])\n"},{"location":"reference/calcipy/tasks/defaults/#calcipy.tasks.defaults.new_collection","title":"new_collection","text":"new_collection()\n Initialize a collection with the combination of merged and project-specific defaults. Source code in calcipy/tasks/defaults.py @beartype\ndef new_collection() -> Collection:\n \"\"\"Initialize a collection with the combination of merged and project-specific defaults.\"\"\"\n ns = Collection('')\n\n # Merge default and user configuration\n ns.configure(DEFAULTS)\n config_path = Path('.calcipy.json')\n if config_path.is_file():\n ns.configure(json.loads(config_path.read_text(encoding='utf-8')))\n\n return ns\n"},{"location":"reference/calcipy/tasks/doc/","title":"doc","text":"Document CLI."},{"location":"reference/calcipy/tasks/doc/#calcipy.tasks.doc-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/doc/#calcipy.tasks.doc.build","title":"build","text":"build(ctx)\n Build documentation with mkdocs. Source code in calcipy/tasks/doc.py @task()\ndef build(ctx: Context) -> None:\n \"\"\"Build documentation with mkdocs.\"\"\"\n auto_doc_path = get_doc_subdir().parent / 'modules'\n write_autoformatted_md_sections()\n delete_dir(auto_doc_path)\n _diagram_task(ctx, auto_doc_path)\n\n # Find and trim trailing whitespace\n for path_md in auto_doc_path.rglob('*.md'):\n trim_trailing_whitespace(path_md)\n\n run(ctx, f'{python_dir()}/mkdocs build --site-dir {get_out_dir()}')\n"},{"location":"reference/calcipy/tasks/doc/#calcipy.tasks.doc.deploy","title":"deploy","text":"deploy(ctx)\n Deploy docs to the Github gh-pages branch. Source code in calcipy/tasks/doc.py @task()\ndef deploy(ctx: Context) -> None:\n \"\"\"Deploy docs to the Github `gh-pages` branch.\"\"\"\n if _is_mkdocs_local(): # pragma: no cover\n raise NotImplementedError('Not yet configured to deploy documentation without \"use_directory_urls\"')\n\n with suppress(UnexpectedExit):\n run(ctx, 'pre-commit uninstall') # To prevent pre-commit failures when mkdocs calls push\n run(ctx, f'{python_dir()}/mkdocs gh-deploy --force')\n with suppress(UnexpectedExit):\n run(ctx, 'pre-commit install') # Restore pre-commit\n"},{"location":"reference/calcipy/tasks/doc/#calcipy.tasks.doc.get_out_dir","title":"get_out_dir","text":"get_out_dir()\n Retrieve the mkdocs-specified site directory. Source code in calcipy/tasks/doc.py @beartype\ndef get_out_dir() -> Path:\n \"\"\"Retrieve the mkdocs-specified site directory.\"\"\"\n mkdocs_config = read_yaml_file(get_project_path() / MKDOCS_CONFIG)\n return Path(mkdocs_config.get('site_dir', 'releases/site'))\n"},{"location":"reference/calcipy/tasks/doc/#calcipy.tasks.doc.watch","title":"watch","text":"watch(ctx)\n Serve local documentation for local editing. Source code in calcipy/tasks/doc.py @task()\ndef watch(ctx: Context) -> None:\n \"\"\"Serve local documentation for local editing.\"\"\"\n if _is_mkdocs_local(): # pragma: no cover\n path_doc_index = get_out_dir() / 'index.html'\n open_in_browser(path_doc_index)\n else: # pragma: no cover\n webbrowser.open('http://localhost:8000')\n run(ctx, f'{python_dir()}/mkdocs serve --dirtyreload')\n"},{"location":"reference/calcipy/tasks/executable_utils/","title":"executable_utils","text":"Utilities for working in calcipy\u2019s python environment."},{"location":"reference/calcipy/tasks/executable_utils/#calcipy.tasks.executable_utils-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/executable_utils/#calcipy.tasks.executable_utils.check_installed","title":"check_installed","text":"check_installed(ctx, executable, message)\n If the required executable isn\u2019t present, raise a clear user error. Source code in calcipy/tasks/executable_utils.py @beartype\ndef check_installed(ctx: Context, executable: str, message: str) -> None:\n \"\"\"If the required executable isn't present, raise a clear user error.\"\"\"\n res = run(ctx, f'which {executable}', warn=True, hide=True)\n if not res or res.exited == 1:\n raise RuntimeError(message)\n"},{"location":"reference/calcipy/tasks/executable_utils/#calcipy.tasks.executable_utils.python_dir","title":"python_dir cached","text":"python_dir()\n Run an executable from the currently active Python directory. Source code in calcipy/tasks/executable_utils.py @lru_cache(maxsize=1)\ndef python_dir() -> str:\n \"\"\"Run an executable from the currently active Python directory.\"\"\"\n return str(resolve_python().parent)\n"},{"location":"reference/calcipy/tasks/executable_utils/#calcipy.tasks.executable_utils.python_m","title":"python_m cached","text":"python_m()\n Return the active python path and -m flag. Source code in calcipy/tasks/executable_utils.py @lru_cache(maxsize=1)\ndef python_m() -> str:\n \"\"\"Return the active python path and `-m` flag.\"\"\"\n return f'{resolve_python()} -m'\n"},{"location":"reference/calcipy/tasks/executable_utils/#calcipy.tasks.executable_utils.resolve_python","title":"resolve_python cached","text":"resolve_python()\n Resolve the user\u2019s Python path based on sys. Source code in calcipy/tasks/executable_utils.py @lru_cache(maxsize=1)\ndef resolve_python() -> Path:\n \"\"\"Resolve the user's Python path based on `sys`.\"\"\"\n python_path = Path(sys.executable)\n with suppress(ValueError):\n return python_path.relative_to(Path.cwd())\n return python_path\n"},{"location":"reference/calcipy/tasks/lint/","title":"lint","text":"Lint CLI."},{"location":"reference/calcipy/tasks/lint/#calcipy.tasks.lint-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/lint/#calcipy.tasks.lint.check","title":"check","text":"check(ctx)\n Run ruff as check-only. Source code in calcipy/tasks/lint.py @task(default=True)\ndef check(ctx: Context) -> None:\n \"\"\"Run ruff as check-only.\"\"\"\n _inner_task(ctx, command='ruff check')\n"},{"location":"reference/calcipy/tasks/lint/#calcipy.tasks.lint.fix","title":"fix","text":"fix(ctx, *, unsafe=False)\n Run ruff and apply fixes. Source code in calcipy/tasks/lint.py @task(\n help={\n 'unsafe': 'if provided, attempt even fixes considered unsafe',\n },\n)\ndef fix(ctx: Context, *, unsafe: bool = False) -> None:\n \"\"\"Run ruff and apply fixes.\"\"\"\n cli_args = '--fix'\n if unsafe:\n cli_args += ' --unsafe-fixes'\n _inner_task(ctx, command='ruff check', cli_args=cli_args)\n"},{"location":"reference/calcipy/tasks/lint/#calcipy.tasks.lint.pre_commit","title":"pre_commit","text":"pre_commit(ctx, *, no_update=False)\n Run pre-commit. Source code in calcipy/tasks/lint.py @task(\n help={\n 'no_update': 'Skip updating the pre-commit hooks',\n },\n)\ndef pre_commit(ctx: Context, *, no_update: bool = False) -> None:\n \"\"\"Run pre-commit.\"\"\"\n check_installed(ctx, executable='pre-commit', message=PRE_COMMIT_MESSAGE)\n\n run(ctx, 'pre-commit install')\n if not no_update:\n run(ctx, 'pre-commit autoupdate')\n\n stages_cli = ' '.join(f'--hook-stage {stg}' for stg in ALL_PRE_COMMIT_HOOK_STAGES)\n run(ctx, f'pre-commit run --all-files {stages_cli}')\n"},{"location":"reference/calcipy/tasks/lint/#calcipy.tasks.lint.pylint","title":"pylint","text":"pylint(ctx, *, report=False)\n Run pylint. Source code in calcipy/tasks/lint.py @task(\n help={\n 'report': 'if provided, show the pylint summary report',\n },\n)\ndef pylint(ctx: Context, *, report: bool = False) -> None:\n \"\"\"Run pylint.\"\"\"\n cli_args = '--report=y' if report else ''\n _inner_task(ctx, command='pylint', cli_args=cli_args)\n"},{"location":"reference/calcipy/tasks/lint/#calcipy.tasks.lint.watch","title":"watch","text":"watch(ctx)\n Run ruff as check-only. Source code in calcipy/tasks/lint.py @task()\ndef watch(ctx: Context) -> None:\n \"\"\"Run ruff as check-only.\"\"\"\n _inner_task(ctx, command='ruff check', cli_args='--watch --show-source')\n"},{"location":"reference/calcipy/tasks/nox/","title":"nox","text":"Nox CLI."},{"location":"reference/calcipy/tasks/nox/#calcipy.tasks.nox-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/nox/#calcipy.tasks.nox.noxfile","title":"noxfile","text":"noxfile(ctx, *, session='')\n Run nox from the local noxfile. Source code in calcipy/tasks/nox.py @task(\n default=True,\n help={\n 'session': 'Optional session to run',\n },\n)\ndef noxfile(ctx: Context, *, session: str = '') -> None:\n \"\"\"Run nox from the local noxfile.\"\"\"\n cli_args = ['--session', session] if session else []\n run(ctx, f'{python_dir()}/nox --error-on-missing-interpreters {\" \".join(cli_args)}')\n"},{"location":"reference/calcipy/tasks/pack/","title":"pack","text":"Packaging CLI."},{"location":"reference/calcipy/tasks/pack/#calcipy.tasks.pack-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/pack/#calcipy.tasks.pack.bump_tag","title":"bump_tag","text":"bump_tag(ctx, *, tag, tag_prefix='', pkg_name='')\n Experiment with bumping the git tag using griffe. Example for calcipy: ./run pack.bump-tag --tag=\"$(git tag -l \"*\" | sort | head -n 5 | tail -n 1)\" --tag-prefix=\"\"\n Source code in calcipy/tasks/pack.py @task(\n help={\n 'tag': 'Last tag, can be provided with `--tag=\"$(git tag -l \"v*\" | sort | tail -n 1)\"`',\n 'tag_prefix': 'Optional tag prefix, such as \"v\"',\n 'pkg_name': 'Optional package name. If not provided, will read the poetry pyproject.toml file',\n },\n)\ndef bump_tag(ctx: Context, *, tag: str, tag_prefix: str = '', pkg_name: str = '') -> None: # noqa: ARG001\n \"\"\"Experiment with bumping the git tag using `griffe`.\n\n Example for `calcipy`:\n\n ```sh\n ./run pack.bump-tag --tag=\"$(git tag -l \"*\" | sort | head -n 5 | tail -n 1)\" --tag-prefix=\"\"\n ```\n\n \"\"\"\n if not tag:\n raise ValueError('tag must not be empty')\n if not pkg_name:\n poetry_config = file_helpers.read_pyproject()['tool']['poetry']\n pkg_name = poetry_config['name']\n\n from calcipy.experiments import bump_programmatically # noqa: PLC0415\n\n new_version = bump_programmatically.bump_tag(\n pkg_name=pkg_name,\n tag=tag,\n tag_prefix=tag_prefix,\n )\n LOGGER.text(new_version)\n"},{"location":"reference/calcipy/tasks/pack/#calcipy.tasks.pack.check_licenses","title":"check_licenses","text":"check_licenses(ctx)\n Check licenses for compatibility with licensecheck. Source code in calcipy/tasks/pack.py @task()\ndef check_licenses(ctx: Context) -> None:\n \"\"\"Check licenses for compatibility with `licensecheck`.\"\"\"\n res = run(ctx, 'which licensecheck', warn=True, hide=True)\n if not res or res.exited == 1:\n LOGGER.warning('`licensecheck` not found. installing with pipx')\n run(ctx, 'pipx install licensecheck')\n run(ctx, 'licensecheck')\n"},{"location":"reference/calcipy/tasks/pack/#calcipy.tasks.pack.install_extras","title":"install_extras","text":"install_extras(ctx)\n Run poetry install with all extras. Source code in calcipy/tasks/pack.py @task(pre=[lock])\ndef install_extras(ctx: Context) -> None:\n \"\"\"Run poetry install with all extras.\"\"\"\n poetry_config = file_helpers.read_pyproject()['tool']['poetry']\n extras = (poetry_config.get('extras') or {}).keys()\n run(ctx, ' '.join(['poetry install --sync', *[f'--extras={ex}' for ex in extras]]))\n"},{"location":"reference/calcipy/tasks/pack/#calcipy.tasks.pack.lock","title":"lock","text":"lock(ctx)\n Ensure poetry.lock is up-to-date. Source code in calcipy/tasks/pack.py @task()\ndef lock(ctx: Context) -> None:\n \"\"\"Ensure poetry.lock is up-to-date.\"\"\"\n if can_skip.can_skip(prerequisites=[PROJECT_TOML], targets=[LOCK]):\n return # Exit early\n\n run(ctx, 'poetry lock --no-update')\n"},{"location":"reference/calcipy/tasks/pack/#calcipy.tasks.pack.publish","title":"publish","text":"publish(ctx, *, to_test_pypi=False)\n Build the distributed format(s) and publish. Source code in calcipy/tasks/pack.py @task(\n help={\n 'to_test_pypi': 'Publish to the TestPyPi repository',\n },\n)\ndef publish(ctx: Context, *, to_test_pypi: bool = False) -> None:\n \"\"\"Build the distributed format(s) and publish.\"\"\"\n run(ctx, f'{python_dir()}/nox --error-on-missing-interpreters --session build_dist build_check')\n\n cmd = 'poetry publish'\n if to_test_pypi:\n cmd += ' --repository testpypi'\n run(ctx, cmd)\n"},{"location":"reference/calcipy/tasks/stale/","title":"stale","text":"Stale Packages CLI."},{"location":"reference/calcipy/tasks/stale/#calcipy.tasks.stale-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/stale/#calcipy.tasks.stale.check_for_stale_packages","title":"check_for_stale_packages","text":"check_for_stale_packages(ctx, *, stale_months=48)\n Identify stale dependencies. Source code in calcipy/tasks/stale.py @task(\n default=True,\n help={\n 'stale_months': 'Cutoff in months for when a package may be stale enough to be a risk',\n },\n)\ndef check_for_stale_packages(ctx: Context, *, stale_months: int = 48) -> None: # noqa: ARG001\n \"\"\"Identify stale dependencies.\"\"\"\n cfsp(stale_months=stale_months)\n"},{"location":"reference/calcipy/tasks/tags/","title":"tags","text":"Code Tag Collector CLI."},{"location":"reference/calcipy/tasks/tags/#calcipy.tasks.tags-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/tags/#calcipy.tasks.tags.collect_code_tags","title":"collect_code_tags","text":"collect_code_tags(ctx, base_dir='.', doc_sub_dir='', filename=None, tag_order='', regex='', ignore_patterns='')\n Create a CODE_TAG_SUMMARY.md with a table for TODO- and FIXME-style code comments. Source code in calcipy/tasks/tags.py @task(\n default=True,\n help={\n 'base_dir': 'Working Directory',\n 'doc_sub_dir': 'Subdirectory for output of the code tag summary file',\n 'filename': 'Code Tag Summary Filename',\n 'tag_order': 'Ordered list of code tags to locate (Comma-separated)',\n 'regex': 'Custom Code Tag Regex. Must contain \"{tag}\"',\n 'ignore_patterns': 'Glob patterns to ignore files and directories when searching (Comma-separated)',\n },\n)\ndef collect_code_tags( # noqa: PLR0917\n ctx: Context,\n base_dir: str = '.',\n doc_sub_dir: str = '',\n filename: Optional[str] = None,\n tag_order: str = '',\n regex: str = '',\n ignore_patterns: str = '',\n) -> None:\n \"\"\"Create a `CODE_TAG_SUMMARY.md` with a table for TODO- and FIXME-style code comments.\"\"\"\n pth_base_dir = Path(base_dir).resolve()\n pth_docs = pth_base_dir / doc_sub_dir if doc_sub_dir else get_doc_subdir()\n if filename and '/' in filename:\n raise RuntimeError('Unexpected slash in filename. You should consider setting `--doc-sub-dir` instead')\n path_tag_summary = pth_docs / (filename or from_ctx(ctx, 'tags', 'filename'))\n patterns = (ignore_patterns or from_ctx(ctx, 'tags', 'ignore_patterns')).split(',')\n paths_source = find_project_files(pth_base_dir, ignore_patterns=[pattern for pattern in patterns if pattern])\n\n write_code_tag_file(\n path_tag_summary=path_tag_summary,\n paths_source=paths_source,\n base_dir=pth_base_dir,\n regex=regex,\n tags=tag_order,\n header='# Collected Code Tags',\n )\n"},{"location":"reference/calcipy/tasks/test/","title":"test","text":"Test CLI."},{"location":"reference/calcipy/tasks/test/#calcipy.tasks.test-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/test/#calcipy.tasks.test.check","title":"check","text":"check(_ctx)\n Run pytest checks, such as identifying . Source code in calcipy/tasks/test.py @task()\ndef check(_ctx: Context) -> None:\n \"\"\"Run pytest checks, such as identifying .\"\"\"\n if duplciates := check_duplicate_test_names.run(Path('tests')):\n raise RuntimeError(f'Duplicate test names found ({duplciates}). See above for details.') # noqa: EM102\n"},{"location":"reference/calcipy/tasks/test/#calcipy.tasks.test.coverage","title":"coverage","text":"coverage(ctx, *, min_cover=0, out_dir=None, view=False)\n Generate useful coverage outputs after running pytest. Creates coverage.json used in doc.build Source code in calcipy/tasks/test.py @task(\n help={\n 'min_cover': 'Fail if coverage less than threshold',\n 'out_dir': 'Optional path to coverage directory. Typically \".cover\" or \"releases/tests\"',\n 'view': 'If True, open the created files',\n },\n)\ndef coverage(ctx: Context, *, min_cover: int = 0, out_dir: Optional[str] = None, view: bool = False) -> None:\n \"\"\"Generate useful coverage outputs after running pytest.\n\n Creates `coverage.json` used in `doc.build`\n\n \"\"\"\n pkg_name = read_package_name()\n _inner_task(ctx, cli_args='', min_cover=min_cover,\n command=f'coverage run --branch --source={pkg_name} --module pytest')\n\n cov_dir = Path(out_dir or from_ctx(ctx, 'test', 'out_dir'))\n cov_dir.mkdir(exist_ok=True, parents=True)\n print() # noqa: T201\n for cli_args in (\n 'report --show-missing', # Write to STDOUT\n f'html --directory={cov_dir}', # Write to HTML\n 'json', # Create coverage.json file for \"_handle_coverage\"\n ):\n run(ctx, f'{python_dir()}/coverage {cli_args}')\n\n if view: # pragma: no cover\n open_in_browser(cov_dir / 'index.html')\n"},{"location":"reference/calcipy/tasks/test/#calcipy.tasks.test.pytest","title":"pytest","text":"pytest(ctx, *, keyword='', marker='', min_cover=0)\n Run pytest with default arguments. Additional arguments can be set in the environment variable \u2018PYTEST_ADDOPTS\u2019 Source code in calcipy/tasks/test.py @task(\n default=True,\n help={\n 'min_cover': 'Fail if coverage less than threshold',\n **KM_HELP,\n },\n)\ndef pytest(ctx: Context, *, keyword: str = '', marker: str = '', min_cover: int = 0) -> None:\n \"\"\"Run pytest with default arguments.\n\n Additional arguments can be set in the environment variable 'PYTEST_ADDOPTS'\n\n \"\"\"\n pkg_name = read_package_name()\n durations = '--durations=25 --durations-min=\"0.1\"'\n _inner_task(ctx,\n cli_args=f' --cov={pkg_name} --cov-branch --cov-report=term-missing {durations}',\n keyword=keyword, marker=marker, min_cover=min_cover)\n"},{"location":"reference/calcipy/tasks/test/#calcipy.tasks.test.step","title":"step","text":"step(ctx, *, keyword='', marker='')\n Run pytest optimized to stop on first error. Source code in calcipy/tasks/test.py @task(help=KM_HELP)\ndef step(ctx: Context, *, keyword: str = '', marker: str = '') -> None:\n \"\"\"Run pytest optimized to stop on first error.\"\"\"\n _inner_task(ctx, cli_args=_STEPWISE_ARGS, keyword=keyword, marker=marker)\n"},{"location":"reference/calcipy/tasks/test/#calcipy.tasks.test.watch","title":"watch","text":"watch(ctx, *, keyword='', marker='')\n Run pytest with polling and optimized to stop on first error. Source code in calcipy/tasks/test.py @task(help=KM_HELP)\ndef watch(ctx: Context, *, keyword: str = '', marker: str = '') -> None:\n \"\"\"Run pytest with polling and optimized to stop on first error.\"\"\"\n _inner_task(ctx, cli_args=_STEPWISE_ARGS, keyword=keyword, marker=marker, command='ptw . --now')\n"},{"location":"reference/calcipy/tasks/types/","title":"types","text":"Types CLI."},{"location":"reference/calcipy/tasks/types/#calcipy.tasks.types-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/types/#calcipy.tasks.types.basedpyright","title":"basedpyright","text":"basedpyright(ctx)\n Run basedpyright. Source code in calcipy/tasks/types.py @task()\ndef basedpyright(ctx: Context) -> None:\n \"\"\"Run basedpyright.\"\"\"\n _inner_task(ctx, command=f'{python_dir()}/basedpyright')\n"},{"location":"reference/calcipy/tasks/types/#calcipy.tasks.types.mypy","title":"mypy","text":"mypy(ctx)\n Run mypy. Source code in calcipy/tasks/types.py @task()\ndef mypy(ctx: Context) -> None:\n \"\"\"Run mypy.\"\"\"\n _inner_task(ctx, command=f'{python_dir()}/mypy')\n"},{"location":"reference/calcipy/tasks/types/#calcipy.tasks.types.pyright","title":"pyright","text":"pyright(ctx)\n Run pyright. Source code in calcipy/tasks/types.py @task()\ndef pyright(ctx: Context) -> None:\n \"\"\"Run pyright.\"\"\"\n check_installed(ctx, executable='pyright', message=PYRIGHT_MESSAGE)\n _inner_task(ctx, command='pyright')\n"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"calcipy","text":"calcipy is a Python package that implements best practices such as code style (linting, auto-fixes), documentation, CI/CD, and logging. Like the calcium carbonate in hard coral, packages can be built on the calcipy foundation. calcipy has some configurability, but is tailored for my particular use cases. If you want the same sort of functionality, there are a number of alternatives to consider: pyscaffold is a much more mature project that aims for the same goals, but with a slightly different approach and tech stack (tox vs. nox, cookiecutter vs. copier, etc.) tidypy, pylama, and codecheck offer similar functionality of bundling and running static checkers, but makes far fewer assumptions pytoil is a general CLI tool for developer automation And many more such as pyta, prospector, wemake-python-styleguide / cjolowicz/cookiecutter-hypermodern-python, formate, johnthagen/python-blueprint, oxsecurity/megalinter, etc. "},{"location":"#installation","title":"Installation","text":"Calcipy needs a few static files managed using copier and a template project: kyleking/calcipy_template You can quickly use the template to create a new project or add calcipy to an existing one: # Install copier. pipx is recommended\npipx install copier\n\n# To create a new project\ncopier copy gh:KyleKing/calcipy_template new_project\ncd new_project\n\n# Or convert/update an existing one\ncd my_project\ncopier copy gh:KyleKing/calcipy_template .\ncopier update\n"},{"location":"#calcipy-cli","title":"Calcipy CLI","text":"Additionally, calcipy can be run as a CLI application without adding the package as a dependency. Quick Start: # For the CLI, only install a few of the extras which can be used from a few different CLI commands\npipx install 'calcipy[lint,tags]'\n\n# Use 'tags' to create a CODE_TAG_SUMMARY of the specified directory\ncalcipy-tags tags --help\ncalcipy-tags tags --base-dir=~/path/to/my_project\n\n# You can list all provided CLI commands with\npipx list\n venvs are in ~/.local/pipx/venvs\napps are exposed on your $PATH at ~/.local/bin\n package calcipy 1.4.0, installed using Python 3.11.4\n - calcipy\n - calcipy-lint\n - calcipy-pack\n - calcipy-tags\n - calcipy-types\n Note: the CLI output below is compressed for readability, but you can try running each of these commands locally to see the most up-to-date documentation and the full set of options. The \u201cUsage\u201d, \u201cCore options\u201d, and \u201cGlobal Task Options\u201d are the same for each subsequent command, so they are excluded for brevity. > calcipy-lint\nUsage: calcipy-lint [--core-opts] <subcommand> [--subcommand-opts] ...\n\nCore options:\n\n --complete Print tab-completion candidates for given parse remainder.\n --hide=STRING Set default value of run()'s 'hide' kwarg.\n --print-completion-script=STRING Print the tab-completion script for your preferred shell (bash|zsh|fish).\n --prompt-for-sudo-password Prompt user at start of session for the sudo.password config value.\n --write-pyc Enable creation of .pyc files.\n -d, --debug Enable debug output.\n -D INT, --list-depth=INT When listing tasks, only show the first INT levels.\n -e, --echo Echo executed commands before running.\n -f STRING, --config=STRING Runtime configuration file to use.\n -F STRING, --list-format=STRING Change the display format used when listing tasks. Should be one of: flat (default), nested, json.\n -h [STRING], --help[=STRING] Show core or per-task help and exit.\n -l [STRING], --list[=STRING] List available tasks, optionally limited to a namespace.\n -p, --pty Use a pty when executing shell commands.\n -R, --dry Echo commands instead of running.\n -T INT, --command-timeout=INT Specify a global command execution timeout, in seconds.\n -V, --version Show version and exit.\n -w, --warn-only Warn, instead of failing, when shell commands fail.\n\nSubcommands:\n\n lint.autopep8 Run autopep8.\n lint.check (lint) Run ruff as check-only.\n lint.fix Run ruff and apply fixes.\n lint.pre-commit Run pre-commit.\n lint.pylint Run pylint.\n lint.security Attempt to identify possible security vulnerabilities.\n lint.watch Run ruff as check-only.\n\nGlobal Task Options:\n\n *file_args List of Paths available globally to all tasks. Will resolve paths with working_dir\n --keep-going Continue running tasks even on failure\n --working_dir=STRING Set the cwd for the program. Example: \"../run --working-dir .. lint test\"\n -v,-vv,-vvv Globally configure logger verbosity (-vvv for most verbose)\n\n> calcipy-pack\n\nSubcommands:\n\n pack.check-licenses Check licenses for compatibility with `licensecheck`.\n pack.install-extras Run poetry install with all extras.\n pack.lock Ensure poetry.lock is up-to-date.\n pack.publish Build the distributed format(s) and publish.\n\n> calcipy-tags\n\nSubcommands:\n\n tags.collect-code-tags (tags) Create a `CODE_TAG_SUMMARY.md` with a table for TODO- and FIXME-style code comments.\n\n> calcipy-types\n\nSubcommands:\n\n types.basedpyright Run basedpyright.\n types.mypy Run mypy.\n types.pyright Run pyright.\n"},{"location":"#calcipy-pre-commit","title":"Calcipy Pre-Commit","text":"calcipy can also be used as a pre-commit task by adding the below snippet to your pre-commit file: repos:\n - repo: https://github.com/KyleKing/calcipy\n rev: main\n hooks:\n - id: tags\n - id: lint-fix\n - id: types\n"},{"location":"#project-status","title":"Project Status","text":"See the Open Issues and/or the CODE_TAG_SUMMARY. For release history, see the CHANGELOG."},{"location":"#contributing","title":"Contributing","text":"We welcome pull requests! For your pull request to be accepted smoothly, we suggest that you first open a GitHub issue to discuss your idea. For resources on getting started with the code base, see the below documentation: DEVELOPER_GUIDE STYLE_GUIDE "},{"location":"#code-of-conduct","title":"Code of Conduct","text":"We follow the Contributor Covenant Code of Conduct."},{"location":"#open-source-status","title":"Open Source Status","text":"We try to reasonably meet most aspects of the \u201cOpenSSF scorecard\u201d from Open Source Insights"},{"location":"#responsible-disclosure","title":"Responsible Disclosure","text":"If you have any security issue to report, please contact the project maintainers privately. You can reach us at dev.act.kyle@gmail.com."},{"location":"#license","title":"License","text":"LICENSE"},{"location":"docs/CHANGELOG/","title":"Docs","text":""},{"location":"docs/CHANGELOG/#unreleased","title":"Unreleased","text":""},{"location":"docs/CHANGELOG/#300-2024-06-07","title":"3.0.0 (2024-06-07)","text":""},{"location":"docs/CHANGELOG/#fix","title":"Fix","text":" finish migration from autopep8 to ruff resolve remaining ruff errors manually use all-caps LOGGER from corallium require lock before install-extras copier-auto-update "},{"location":"docs/CHANGELOG/#refactor","title":"Refactor","text":" unsafe fixes from ruff safe fixes from ruff "},{"location":"docs/CHANGELOG/#220rc0-2024-06-06","title":"2.2.0rc0 (2024-06-06)","text":""},{"location":"docs/CHANGELOG/#feat","title":"Feat","text":" remove bandit/security task remove flake8 "},{"location":"docs/CHANGELOG/#fix_1","title":"Fix","text":" make experimental dependencies optional remove pip-check shorten main task list remove autopep8 remove semgrep "},{"location":"docs/CHANGELOG/#210-2024-04-17","title":"2.1.0 (2024-04-17)","text":""},{"location":"docs/CHANGELOG/#feat_1","title":"Feat","text":" add basedpyright "},{"location":"docs/CHANGELOG/#fix_2","title":"Fix","text":" exclude plantuml package 1.10.0 "},{"location":"docs/CHANGELOG/#refactor_1","title":"Refactor","text":" replace filter with iterator "},{"location":"docs/CHANGELOG/#204-2024-01-31","title":"2.0.4 (2024-01-31)","text":""},{"location":"docs/CHANGELOG/#fix_3","title":"Fix","text":" add \u2013doc-sub-dir for Code Tag generation without copier-answers create the code tag summary directory if it doesn\u2019t already exist "},{"location":"docs/CHANGELOG/#203-2023-11-21","title":"2.0.3 (2023-11-21)","text":""},{"location":"docs/CHANGELOG/#fix_4","title":"Fix","text":" pin a recent version of virtualenv for Python 3.12 + Nox "},{"location":"docs/CHANGELOG/#202-2023-11-08","title":"2.0.2 (2023-11-08)","text":""},{"location":"docs/CHANGELOG/#refactor_2","title":"Refactor","text":" copier-auto-update "},{"location":"docs/CHANGELOG/#201-2023-09-08","title":"2.0.1 (2023-09-08)","text":""},{"location":"docs/CHANGELOG/#fix_5","title":"Fix","text":" add missing setuptools for some mkdocs dependencies add calcipy-docs and restore missing extras for type checking add scoped \u2018calcipy-test\u2019 "},{"location":"docs/CHANGELOG/#refactor_3","title":"Refactor","text":" really move nox use ruff.external to allow list flake8 rules "},{"location":"docs/CHANGELOG/#200-2023-09-02","title":"2.0.0 (2023-09-02)","text":""},{"location":"docs/CHANGELOG/#feat_2","title":"Feat","text":" drop Python 3.8 "},{"location":"docs/CHANGELOG/#fix_6","title":"Fix","text":" specify a unique name when binding new kwargs "},{"location":"docs/CHANGELOG/#164-2023-08-28","title":"1.6.4 (2023-08-28)","text":""},{"location":"docs/CHANGELOG/#fix_7","title":"Fix","text":" update pyrate-limiter "},{"location":"docs/CHANGELOG/#refactor_4","title":"Refactor","text":" address pydantic v2 migration warnings "},{"location":"docs/CHANGELOG/#163-2023-08-13","title":"1.6.3 (2023-08-13)","text":""},{"location":"docs/CHANGELOG/#fix_8","title":"Fix","text":" remove redundant beartype to lru_cache "},{"location":"docs/CHANGELOG/#refactor_5","title":"Refactor","text":" resolve asdf tried get_env_vars "},{"location":"docs/CHANGELOG/#162-2023-08-12","title":"1.6.2 (2023-08-12)","text":""},{"location":"docs/CHANGELOG/#161rc0-2023-08-12","title":"1.6.1rc0 (2023-08-12)","text":""},{"location":"docs/CHANGELOG/#fix_9","title":"Fix","text":" support partial kwargs for pre/post defer Task to support decorator chaining "},{"location":"docs/CHANGELOG/#refactor_6","title":"Refactor","text":" setup for deferred task refactor update dependency versions update copier for a more generic environment variable "},{"location":"docs/CHANGELOG/#160-2023-07-23","title":"1.6.0 (2023-07-23)","text":""},{"location":"docs/CHANGELOG/#feat_3","title":"Feat","text":" experiment with griffe for version bumping "},{"location":"docs/CHANGELOG/#fix_10","title":"Fix","text":" resolve minor bugs in griffe experiment remove dependency on flake8-beartype "},{"location":"docs/CHANGELOG/#refactor_7","title":"Refactor","text":" downgrade replacement machine error to warning "},{"location":"docs/CHANGELOG/#150-2023-07-22","title":"1.5.0 (2023-07-22)","text":""},{"location":"docs/CHANGELOG/#feat_4","title":"Feat","text":" add configurable ignore patterns for tags "},{"location":"docs/CHANGELOG/#refactor_8","title":"Refactor","text":" resolve pylint warnings run bump-pydantic "},{"location":"docs/CHANGELOG/#142-2023-07-16","title":"1.4.2 (2023-07-16)","text":""},{"location":"docs/CHANGELOG/#fix_11","title":"Fix","text":" use the correct exclude rule for semgrep support pydantic v2 serialization skip SemGrep rule to pin GitHub Actions to commit IDs "},{"location":"docs/CHANGELOG/#refactor_9","title":"Refactor","text":" resolve local test suite problems "},{"location":"docs/CHANGELOG/#141-2023-07-07","title":"1.4.1 (2023-07-07)","text":""},{"location":"docs/CHANGELOG/#fix_12","title":"Fix","text":" retrieve only unique python versions from .tool-versions "},{"location":"docs/CHANGELOG/#140-2023-06-25","title":"1.4.0 (2023-06-25)","text":""},{"location":"docs/CHANGELOG/#feat_5","title":"Feat","text":" add calcipy-pack to use install-extras without the extras "},{"location":"docs/CHANGELOG/#fix_13","title":"Fix","text":" show a readable warning when gh, pyright, and/or pre-commit are not installed use calcipy\u2019s python environment when installed with pipx "},{"location":"docs/CHANGELOG/#130-2023-06-21","title":"1.3.0 (2023-06-21)","text":""},{"location":"docs/CHANGELOG/#feat_6","title":"Feat","text":" switch to httpx for async requests "},{"location":"docs/CHANGELOG/#perf","title":"Perf","text":" lazy cache compiling the regex "},{"location":"docs/CHANGELOG/#126-2023-06-17","title":"1.2.6 (2023-06-17)","text":""},{"location":"docs/CHANGELOG/#fix_14","title":"Fix","text":" handle missing git remotes in code tag collection always re-cache packages if empty ignore new TD002 & TD003 linting rules "},{"location":"docs/CHANGELOG/#refactor_10","title":"Refactor","text":" try to handle exceptions when no git origin URL "},{"location":"docs/CHANGELOG/#125-2023-05-23","title":"1.2.5 (2023-05-23)","text":""},{"location":"docs/CHANGELOG/#fix_15","title":"Fix","text":" resolve typing and import errors upgrade to invoke 2.1.2 "},{"location":"docs/CHANGELOG/#refactor_11","title":"Refactor","text":" suppress ruff autofix "},{"location":"docs/CHANGELOG/#124-2023-05-16","title":"1.2.4 (2023-05-16)","text":""},{"location":"docs/CHANGELOG/#fix_16","title":"Fix","text":" add pack.install-extras bump minimum pymdown dependency pydantic alias doesn\u2019t work like that refactor for higher pylint quality update the help string to include \u2013keep-going support branch coverage move pyright to \u2018main\u2019 tasks "},{"location":"docs/CHANGELOG/#123-2023-04-08","title":"1.2.3 (2023-04-08)","text":""},{"location":"docs/CHANGELOG/#fix_17","title":"Fix","text":" handle dev installs when only calcipy "},{"location":"docs/CHANGELOG/#122-2023-04-08","title":"1.2.2 (2023-04-08)","text":""},{"location":"docs/CHANGELOG/#fix_18","title":"Fix","text":" add preview of \u2013keep-going "},{"location":"docs/CHANGELOG/#refactor_12","title":"Refactor","text":" extract task logic from wrapper "},{"location":"docs/CHANGELOG/#121-2023-04-07","title":"1.2.1 (2023-04-07)","text":""},{"location":"docs/CHANGELOG/#fix_19","title":"Fix","text":" skip assert_used in bandit "},{"location":"docs/CHANGELOG/#120-2023-04-06","title":"1.2.0 (2023-04-06)","text":""},{"location":"docs/CHANGELOG/#feat_7","title":"Feat","text":" add test.check with duplicate name logic "},{"location":"docs/CHANGELOG/#fix_20","title":"Fix","text":" remove lru_cache, which was causing mypy errors "},{"location":"docs/CHANGELOG/#111-2023-04-06","title":"1.1.1 (2023-04-06)","text":""},{"location":"docs/CHANGELOG/#fix_21","title":"Fix","text":" resolve linting errors use copier copy "},{"location":"docs/CHANGELOG/#110-2023-04-06","title":"1.1.0 (2023-04-06)","text":""},{"location":"docs/CHANGELOG/#feat_8","title":"Feat","text":" add experimental check duplicate test names "},{"location":"docs/CHANGELOG/#fix_22","title":"Fix","text":" suppress unexpected exit errors in update docs skip pre-commit uninstall for doc.deploy when pre-commit not available skip pre-commit on doc.deploy "},{"location":"docs/CHANGELOG/#101-2023-03-01","title":"1.0.1 (2023-03-01)","text":""},{"location":"docs/CHANGELOG/#fix_23","title":"Fix","text":" bump minimum ruff version "},{"location":"docs/CHANGELOG/#100-2023-02-25","title":"1.0.0 (2023-02-25)","text":""},{"location":"docs/CHANGELOG/#fix_24","title":"Fix","text":" lower default logging level "},{"location":"docs/CHANGELOG/#100rc7-2023-02-23","title":"1.0.0rc7 (2023-02-23)","text":""},{"location":"docs/CHANGELOG/#feat_9","title":"Feat","text":" add lint.security to pre-commit lint-fix hook "},{"location":"docs/CHANGELOG/#fix_25","title":"Fix","text":" remove types hook, which doesn\u2019t work when not local "},{"location":"docs/CHANGELOG/#refactor_13","title":"Refactor","text":" make task summary optional configure start program inline "},{"location":"docs/CHANGELOG/#100rc6-2023-02-22","title":"1.0.0rc6 (2023-02-22)","text":""},{"location":"docs/CHANGELOG/#fix_26","title":"Fix","text":" provide subcommands to support pre-commit "},{"location":"docs/CHANGELOG/#100rc5-2023-02-22","title":"1.0.0rc5 (2023-02-22)","text":""},{"location":"docs/CHANGELOG/#feat_10","title":"Feat","text":" add pre-commit hooks and update docs "},{"location":"docs/CHANGELOG/#fix_27","title":"Fix","text":" handle KeyError when not yet on PyPi\u2019s JSON API "},{"location":"docs/CHANGELOG/#refactor_14","title":"Refactor","text":" sync with corallium ruff and remove flake8 class-attr-order "},{"location":"docs/CHANGELOG/#100rc4-2023-02-22","title":"1.0.0rc4 (2023-02-22)","text":""},{"location":"docs/CHANGELOG/#fix_28","title":"Fix","text":" fully support nox and corallium "},{"location":"docs/CHANGELOG/#100rc3-2023-02-22","title":"1.0.0rc3 (2023-02-22)","text":""},{"location":"docs/CHANGELOG/#fix_29","title":"Fix","text":" add depedency on corallium "},{"location":"docs/CHANGELOG/#100rc2-2023-02-21","title":"1.0.0rc2 (2023-02-21)","text":""},{"location":"docs/CHANGELOG/#feat_11","title":"Feat","text":" merge shoal source code into calcipy "},{"location":"docs/CHANGELOG/#100rc1-2023-02-21","title":"1.0.0rc1 (2023-02-21)","text":""},{"location":"docs/CHANGELOG/#fix_30","title":"Fix","text":" upgrade shoal dependency and add summary add pylint to document move autopep8 to the lint extras "},{"location":"docs/CHANGELOG/#100rc0-2023-02-20","title":"1.0.0rc0 (2023-02-20)","text":""},{"location":"docs/CHANGELOG/#feat_12","title":"Feat","text":" fully support working-dir by wrapping ctx.run recursively find the tool versions file add dont_skip for testing add tests for code tag collector replace dg with user configuration file better support min-cover unit test write_autoformatted_md_sections start using check_imports support prereleases and add autopep8 introduce cl namespace run lint on file_args and use absolufy add initial version of doc namespace remove pre-commit; add doc and ddict extras add progress indicator to tasks add lint.security add ./run to generlize local entrypoint add lint.pre-commit restore most of noxfile run mypy-silent and add main task use can_skip for the package_lock file start manually fixing lint issues add linting migrate over stale package logic add pyright, mypy, and DEFAULTS add write_json and cached pyproject reader add pytest-based tasks and first unit tests experiment with parallel processing init placeholders for new namespaces add can_skip add initial nox task to invoke switch to invoke start restoring files and switching to shoal add shoal "},{"location":"docs/CHANGELOG/#fix_31","title":"Fix","text":" correct ignore pattern logic and expand task tests get doc and cl tasks working code tag summary and rename default tasks properly map CLI arguments to code tag collector show all URLs on the same line in CTC write Code Tags to docs/docs resolve issues with test.watch-json "},{"location":"docs/CHANGELOG/#refactor_15","title":"Refactor","text":" update Progress task extract use_pty to a default in shoal generlize find_in_parents for pyproject.toml as well introduce logger.print and print_debug drop absolufy-imports task simplify configuration fix remaining pyright and flake8 errors rename write-json to coverage resolve pylint issues reorder main tasks resolve flake8 warnings and better manage filenames apply autopep8 to fix whitespace issues import initialized logger directly try to wrap running tasks programmatically\u2026 run prc main move log configuration to shoal.cli.task resolve typecheck errors manually resolve additional linting errors make lint.check default extract start program to shoal another round of manual lint corrections move can_skip to shoal nest invoke-specific code in tasks/ drop pytest-html and conftest completely "},{"location":"docs/CHANGELOG/#0216-2023-01-31","title":"0.21.6 (2023-01-31)","text":""},{"location":"docs/CHANGELOG/#feat_13","title":"Feat","text":" use the \u2013now flag for ptw! "},{"location":"docs/CHANGELOG/#refactor_16","title":"Refactor","text":" remove autoflake in favor of unimport "},{"location":"docs/CHANGELOG/#0215-2022-11-27","title":"0.21.5 (2022-11-27)","text":""},{"location":"docs/CHANGELOG/#fix_32","title":"Fix","text":" escape % in yaml files as well "},{"location":"docs/CHANGELOG/#0214-2022-11-20","title":"0.21.4 (2022-11-20)","text":""},{"location":"docs/CHANGELOG/#fix_33","title":"Fix","text":" handle % in path names for ctt suppress code tag failures when not a git directory add support for tomllib in Python 3.11! "},{"location":"docs/CHANGELOG/#refactor_17","title":"Refactor","text":" copier update "},{"location":"docs/CHANGELOG/#0213-2022-11-13","title":"0.21.3 (2022-11-13)","text":""},{"location":"docs/CHANGELOG/#fix_34","title":"Fix","text":" remove unimplemented diff-cover exit 1 on semgrep errors "},{"location":"docs/CHANGELOG/#0212-2022-11-12","title":"0.21.2 (2022-11-12)","text":""},{"location":"docs/CHANGELOG/#fix_35","title":"Fix","text":" add relative path prefix for linting \u2018./\u2019 ensure that cz bump doesn\u2019t break the requirements file bump minimum pip-check "},{"location":"docs/CHANGELOG/#0211-2022-11-05","title":"0.21.1 (2022-11-05)","text":""},{"location":"docs/CHANGELOG/#fix_36","title":"Fix","text":" install poetry for \u2018doit publish\u2019 bump minimum mkdocs for deploy_docs "},{"location":"docs/CHANGELOG/#0210-2022-11-05","title":"0.21.0 (2022-11-05)","text":""},{"location":"docs/CHANGELOG/#feat_14","title":"Feat","text":" remove if-expr and other unnecessary new flake8 plugins merge extra flake8 packages based on flake8-aggressive "},{"location":"docs/CHANGELOG/#fix_37","title":"Fix","text":" prevent line break in table for Code Tag summary add pdbr with rich to replace pdbpp "},{"location":"docs/CHANGELOG/#0201-2022-10-19","title":"0.20.1 (2022-10-19)","text":""},{"location":"docs/CHANGELOG/#fix_38","title":"Fix","text":" drop pdbpp and add flake8-fine-pytest "},{"location":"docs/CHANGELOG/#0200-2022-10-16","title":"0.20.0 (2022-10-16)","text":""},{"location":"docs/CHANGELOG/#feat_15","title":"Feat","text":" sign commitizen tags add plantuml generation to mkdocs "},{"location":"docs/CHANGELOG/#fix_39","title":"Fix","text":" remove version conflict with flake8-simplify and cl_bump "},{"location":"docs/CHANGELOG/#0191-2022-10-13","title":"0.19.1 (2022-10-13)","text":""},{"location":"docs/CHANGELOG/#fix_40","title":"Fix","text":" don\u2019t auto-install types "},{"location":"docs/CHANGELOG/#0190-2022-10-06","title":"0.19.0 (2022-10-06)","text":""},{"location":"docs/CHANGELOG/#feat_16","title":"Feat","text":" support optional logging of arguments "},{"location":"docs/CHANGELOG/#fix_41","title":"Fix","text":" split mypy install from mypy run "},{"location":"docs/CHANGELOG/#0180-2022-09-27","title":"0.18.0 (2022-09-27)","text":""},{"location":"docs/CHANGELOG/#feat_17","title":"Feat","text":" support Arrow in pydantic and mypy (ArrowType was causing errors) remove pdoc(s) from document task add gen_ref_nav! tried pybetter, but too focused on adding all #102: sort-of-replace pdocs, but pdoc only shows one function add mypy install argument expand flake8 and reduce tests "},{"location":"docs/CHANGELOG/#fix_42","title":"Fix","text":" re-run prdc lower fail-under for diff. Raise for regular test standardize on a single doit task list Pathlib.absolute is not documented. Use .resolve "},{"location":"docs/CHANGELOG/#refactor_18","title":"Refactor","text":" add type hints and minor changes for mypy make python files non-executable (chmod -x) "},{"location":"docs/CHANGELOG/#0171-2022-09-22","title":"0.17.1 (2022-09-22)","text":""},{"location":"docs/CHANGELOG/#fix_43","title":"Fix","text":" pytest cache assert circular reference "},{"location":"docs/CHANGELOG/#refactor_19","title":"Refactor","text":" move check_security from nox into doit "},{"location":"docs/CHANGELOG/#0170-2022-09-17","title":"0.17.0 (2022-09-17)","text":""},{"location":"docs/CHANGELOG/#feat_18","title":"Feat","text":" add docformat make pyupgrade flag configurable based on minimum python version replace pendulum with arrow update to latest copier "},{"location":"docs/CHANGELOG/#fix_44","title":"Fix","text":" move yamllint configuration to project-specific config naive datetimes can\u2019t be subtracted on Windows hack together a dictionary instead of punq drop safety because of false positives on Calcipy CalVer "},{"location":"docs/CHANGELOG/#refactor_20","title":"Refactor","text":" experiment with custom semgrep rules formalize solution from last commit try to fix punq by switching to functions fix one test, refactor a little, but punq is still not working begin replacing attrs with pydantic update semgrep nox task upgrade copier with linting fixes "},{"location":"docs/CHANGELOG/#0160-2022-08-04","title":"0.16.0 (2022-08-04)","text":""},{"location":"docs/CHANGELOG/#feat_19","title":"Feat","text":" add flake8-simplify "},{"location":"docs/CHANGELOG/#fix_45","title":"Fix","text":" show only date in coverage table "},{"location":"docs/CHANGELOG/#0150-2022-08-03","title":"0.15.0 (2022-08-03)","text":""},{"location":"docs/CHANGELOG/#fix_46","title":"Fix","text":" resolve pyroma errors by installing poetry as a module "},{"location":"docs/CHANGELOG/#0150rc0-2022-07-24","title":"0.15.0rc0 (2022-07-24)","text":""},{"location":"docs/CHANGELOG/#feat_20","title":"Feat","text":" init update workflow and toml sorter create script to bump toml minimum requirements "},{"location":"docs/CHANGELOG/#fix_47","title":"Fix","text":" properly handle missing datetime try to use separate sqlite files for pre-commit and doit correct safety arguments use the unversioned API for releases try to handle pypi response when no \u201creleases\u201d handle prefix of \u201c*\u201d improve error message when \u201creleases\u201d not present use sqlite3 for pre-commit concurrent doit access bump Python to 3.8.4 and drop 3.7 "},{"location":"docs/CHANGELOG/#0145-2022-03-05","title":"0.14.5 (2022-03-05)","text":""},{"location":"docs/CHANGELOG/#fix_48","title":"Fix","text":" #91: prevent minified files from appearing in Code Tag Summary "},{"location":"docs/CHANGELOG/#0144-2022-03-01","title":"0.14.4 (2022-03-01)","text":""},{"location":"docs/CHANGELOG/#fix_49","title":"Fix","text":" set upper limit to fix flake8-bandit compat "},{"location":"docs/CHANGELOG/#0143-2022-03-01","title":"0.14.3 (2022-03-01)","text":""},{"location":"docs/CHANGELOG/#fix_50","title":"Fix","text":" suppress only known git blame errors use Github tables to prevent multi-line rows in Code Tag Summary "},{"location":"docs/CHANGELOG/#0142-2022-02-27","title":"0.14.2 (2022-02-27)","text":""},{"location":"docs/CHANGELOG/#fix_51","title":"Fix","text":" don\u2019t allow multi-line tables "},{"location":"docs/CHANGELOG/#0141-2022-02-27","title":"0.14.1 (2022-02-27)","text":""},{"location":"docs/CHANGELOG/#fix_52","title":"Fix","text":" lower pandas constraint for better 3.7 support drop generation of requirements.txt "},{"location":"docs/CHANGELOG/#0140-2022-02-27","title":"0.14.0 (2022-02-27)","text":""},{"location":"docs/CHANGELOG/#feat_21","title":"Feat","text":" use next-gen attrs syntax improve code tag regex "},{"location":"docs/CHANGELOG/#fix_53","title":"Fix","text":" correct type annotations undo silent nox step make coverage.json optional for task_doc "},{"location":"docs/CHANGELOG/#0130-2022-02-23","title":"0.13.0 (2022-02-23)","text":""},{"location":"docs/CHANGELOG/#feat_22","title":"Feat","text":" drop AppVeyor and update with copier #67: initialize GH CI Action "},{"location":"docs/CHANGELOG/#fix_54","title":"Fix","text":" drop appveyor from cz version files attempt to resolve UnicodeError on Windows action address more minor CI errors make yamllint more relaxed attempt to resolve CI issues remove diff-quality check until fixed for CI always use Interactive when using /dev/null run push hook stage and format subproject support Python 3.7.12 don\u2019t suppress nox missing interpreter errors "},{"location":"docs/CHANGELOG/#refactor_21","title":"Refactor","text":" pre-commit can be run in one command resolve LGTM error "},{"location":"docs/CHANGELOG/#0122-2022-02-21","title":"0.12.2 (2022-02-21)","text":""},{"location":"docs/CHANGELOG/#fix_55","title":"Fix","text":" skip cassette files "},{"location":"docs/CHANGELOG/#0121-2022-02-18","title":"0.12.1 (2022-02-18)","text":""},{"location":"docs/CHANGELOG/#fix_56","title":"Fix","text":" always return 0 to complete format task use short paths when formatting Python code if available "},{"location":"docs/CHANGELOG/#0120-2022-02-18","title":"0.12.0 (2022-02-18)","text":""},{"location":"docs/CHANGELOG/#feat_23","title":"Feat","text":" initialize calcipy pre-commit hook "},{"location":"docs/CHANGELOG/#0110-2022-02-18","title":"0.11.0 (2022-02-18)","text":""},{"location":"docs/CHANGELOG/#feat_24","title":"Feat","text":" don\u2019t verify on cz_bump "},{"location":"docs/CHANGELOG/#refactor_22","title":"Refactor","text":" format fixes reduce noise on matched vulnerabilities make run_cmd public remove jsonlint action use beartype typing move pre-commit hooks into doit "},{"location":"docs/CHANGELOG/#0100-2022-01-19","title":"0.10.0 (2022-01-19)","text":""},{"location":"docs/CHANGELOG/#feat_25","title":"Feat","text":" create Github release if gh CLI is installed "},{"location":"docs/CHANGELOG/#091-2022-01-17","title":"0.9.1 (2022-01-17)","text":""},{"location":"docs/CHANGELOG/#fix_57","title":"Fix","text":" correct code-tag-collector for CLI use use target file\u2019s directory for git info "},{"location":"docs/CHANGELOG/#refactor_23","title":"Refactor","text":" drop template echo command "},{"location":"docs/CHANGELOG/#090-2022-01-17","title":"0.9.0 (2022-01-17)","text":""},{"location":"docs/CHANGELOG/#feat_26","title":"Feat","text":" show code tag summary as a table add date of last blame to code tag summary only link code tag from line number use revision-specific commit hashes add git links to code tag summary add python format pre-commit hook add support for TOML formatting with taplo "},{"location":"docs/CHANGELOG/#fix_58","title":"Fix","text":" handle \u201c0000..\u201d hash by using branch name handle non-git directories use correct line number for pinned hash handle git dependencies when checking stale packages use positional arguments for pre-commit commands correct syntax error in pre-commit command "},{"location":"docs/CHANGELOG/#refactor_24","title":"Refactor","text":" make slow python pre-commit push-only apply auto-format tools to test project "},{"location":"docs/CHANGELOG/#082-2022-01-16","title":"0.8.2 (2022-01-16)","text":""},{"location":"docs/CHANGELOG/#fix_59","title":"Fix","text":" drop all references to CalVer "},{"location":"docs/CHANGELOG/#081-2022-01-16","title":"0.8.1 (2022-01-16)","text":""},{"location":"docs/CHANGELOG/#refactor_25","title":"Refactor","text":" drop tag_format and use semver-only "},{"location":"docs/CHANGELOG/#080-2022-01-16","title":"0.8.0 (2022-01-16)","text":""},{"location":"docs/CHANGELOG/#feat_27","title":"Feat","text":" add pip-check for prettier outdated add cct command initialize the cement CLI application replace toml with tomli (#74) move wily to nox to reduce version conflicts add pip-audit add attrs_strict trim trailing whitespace from doc output "},{"location":"docs/CHANGELOG/#fix_60","title":"Fix","text":" bump year to 2022 code_tag_collector must return doit-compliant value (None) only run pre-commit once Code Tag Collector cannot return a Path for DoIt tasks cct was not being created as an alias. Use full command wily properly exclude auto-generated doc files from code tag collector run ptw on whole directory, not just test files drop FYI and NOTE from Code Tags "},{"location":"docs/CHANGELOG/#refactor_26","title":"Refactor","text":" fix PY-W2000 by using all move file_search to top-level just run pre-commit install replace pass with \u2026 to keep it from being removed "},{"location":"docs/CHANGELOG/#060rc2-2022-08-04","title":"0.6.0rc2 (2022-08-04)","text":""},{"location":"docs/CHANGELOG/#feat_28","title":"Feat","text":" drop pytest-cov and run coverage directly "},{"location":"docs/CHANGELOG/#fix_61","title":"Fix","text":" swap pytest-watch(er) and other version bumps pin min-version (xenon) & drop preconvert and flake8-mock "},{"location":"docs/CHANGELOG/#060rc1-2022-08-04","title":"0.6.0rc1 (2022-08-04)","text":""},{"location":"docs/CHANGELOG/#fix_62","title":"Fix","text":" bug in noxfile and code tags reading doc output files "},{"location":"docs/CHANGELOG/#060rc0-2022-08-04","title":"0.6.0rc0 (2022-08-04)","text":""},{"location":"docs/CHANGELOG/#feat_29","title":"Feat","text":" implement pylint rules add xenon (wraps radon) condense doit output in show_cmd initial semgrep implementation add code diagrams to doc-site add notes on wily and pylint add vulture diff-quality (lint) add it add output if no stale packages found add autoflake "},{"location":"docs/CHANGELOG/#fix_63","title":"Fix","text":" try to fix issues found with pytest_cache_assert drop unused Python2 demjson error in noxfile with poetry install "},{"location":"docs/CHANGELOG/#refactor_27","title":"Refactor","text":" decouple code tag collector from DG improve lint_py and the return type check is working zip release task cleanup a few minor errors use Interactive instead of Chrome apply fixes found from semgrep rename \u201cwrappers\u201d to \u201cdot_dict\u201d prevent mutation in DG.set_paths "},{"location":"docs/CHANGELOG/#031rc0-2022-08-04","title":"0.3.1rc0 (2022-08-04)","text":""},{"location":"docs/CHANGELOG/#feat_30","title":"Feat","text":" add task to zip artifacts from testing implement pandas.to_markdown for cov table #58: implement doc task and merge serve_docs make loading YAML files more generic create doit summary report add task to check license compliance move lock into doit tasks with file-dependency #38: re-implement coverage and write source drop subprocess-tee #36: WIP implementation of doc formatting implement publish tasks add pip outdated to stale check implement check for stale packages begin implementing stale package check smart default tasks for CI vs. local make nox imports optional try doit rather than CWD for initial path move noxfile into calcipy integrate nox to doit tasks WIP check for stale dep & placeholder publish add additional configuration options add nox tasks add detect-secrets as pre-commit lint non-Python files read doc_dir from copier file make most settings configurable added dotted-dict wrapper of Box delete old files rather than full directory filter with glob-like ignore_patterns (2/2) add security check task add skip_phrase for code tag skipping beartype - roar! optionally clear log directory. Move file_helpers from base new tail-like reverse read_lines rename DIG to DG because doit is one word retrieve doc_dir from copier add log-setup fun for doit make (source) doc_dir configurable add sanitizer add pytype #36: start WIP ReadMeMachine #36: merge logic of markdown auto-formatters make code tag partially configurable move check_types into calcipy import templates from pdocs "},{"location":"docs/CHANGELOG/#fix_64","title":"Fix","text":" install full dev-dep as temporary nox workaround prevent committing changelog at base dir remove type_name from docstring for Google-style run pytest as a module for nox resolve doc_dir and style errors #58: remove None from pdocs output deconflict doit/nox repair failing test packaging needed keyword argument import and nox syntax errors run push pre-commit hooks with doit show log warning instead of error for stale dep improve output from check_stale errors caught in testing suppress beartype warnings for now drop logger middleware catch most calcipy section typos in toml remove check-secrets but keep snippets errors in ignore patterns import doit and expand nox test file skip tags must be at bottom reset Lint paths instead of extend use find_files for code tag summary don\u2019t manually add the package source dir lint typing and improve tests create separate file_search file to fix imports #51: replace glob with git-based file identification #53: Use Interact instead of LongRunning undo PEP585 for runtime beartype resolve file explosion from _find_files import the loguru Logger class safely additional problems found with mypy fix Doit Types and start beartype add no-verify to cl_bump correct isort configuration restore ReadMeMachine repair small bugs found in tests make transitions optional "},{"location":"docs/CHANGELOG/#refactor_28","title":"Refactor","text":" see if only one space is okay for skipcq fix anti-pattern with nox session decorator and arg improve code quality try to suppress deepsource errors address DeepSource issues split up set_paths from DG fix formatting error from pre-push rename doc_dir to doc_sub_dir for clarity apply 0.0.10 template move markdown to subdirectory for mkdocs move isort back to toml fix edge case in diff-cov failing and lint errors relicense with MIT for better compliance create generic doit-runner for noxfile fix some type issues minor fixes from AppVeyor testing #38: reduce complexity minor cleanup to docs apply pre-commit autoformat auto-drop skipcq comment format with VSCode fix lint errors in YAML files move find file paths to DG.meta move if_found_unlink to file_helpers fix formatting with pre-commit fix attribute names for path types (1/2) use calcipy:skip-tags fix minor code tags move __temp_chdir and improve fix_dg narrow type ignore use use path_file of file_path apply auto-fixes from pre-commit replace all glob-search with find_files remove find_files from code_tag script update isort for trailing-comma move excluded lint rules to DG apply PEP585 and add pre-commit hook fix capitalization for doit (one word, all lowercase) improve activate_debug_logging try to replace Any with BaseAction apply 0.0.2 minor fixes apply 0.0.1 version of calcipy_template minor renaming for _MarkdownMachine fix lock file and line length run more pre-commit checks on commit standardize on code tags and cleanup "},{"location":"docs/CHANGELOG/#perf_1","title":"Perf","text":" combine autopep8 paths into single command combine files for linting in one command "},{"location":"docs/CHANGELOG/#020a0-2022-08-04","title":"0.2.0a0 (2022-08-04)","text":""},{"location":"docs/CHANGELOG/#feat_31","title":"Feat","text":" push tags with no pre-commit hooks on pre use Interactive instead of \u2014yes for cl_bump* remove tag create/remove tasks new task cl_bump_pre replace MIT license with Unlicense use cz_legacy to generate changelogs remove task in anticipation of copier #26 improve git pre-commit hooks new add-trailing-comma and pyupgrade hooks new mkdocs tasks and improvements new optional preconvert to serialize logs move logger configuration to log_helpers new cl_bump task. Closes #21 "},{"location":"docs/CHANGELOG/#fix_65","title":"Fix","text":" do not pass filenames to pre-commit #43: add year to version for pseudo-calver prevent legacy types for new commits prevent circular import in doit_tasks extras need to be defined as optional rollback hook changes as they are not working install hooks for push LongRunning passed tasks that should fail yesqa removed necessary noqa (H303, etc) incorrect output paths reduce false tags found (WIP). Fix #24 regression in lint_project tasks unincremented version in toml "},{"location":"docs/CHANGELOG/#refactor_29","title":"Refactor","text":" #22: restore MIT license copy+paste unmodified labels workflow update local TODO notes make toml an optional import rename path_source to path_project rename DIG_CWD as PATH_TEST_PROJECT rename test file doit is lowercase (CC looks like Dolt) replace sh with subprocess-tee reduce excess logging move DOIT_CONFIG to import move dig test to dig test file "},{"location":"docs/CHANGELOG/#010-2020-12-19","title":"0.1.0 (2020-12-19)","text":""},{"location":"docs/CHANGELOG/#feat_32","title":"Feat","text":" new write_cl task utilizing comittizen new task, pre_commit_hooks "},{"location":"docs/CHANGELOG/#refactor_30","title":"Refactor","text":" show STDOUT formatting in DoIt task remove archived code "},{"location":"docs/CHANGELOG/#003-2020-12-10","title":"0.0.3 (2020-12-10)","text":""},{"location":"docs/CHANGELOG/#002-2020-11-14","title":"0.0.2 (2020-11-14)","text":""},{"location":"docs/CHANGELOG/#001-2020-11-14","title":"0.0.1 (2020-11-14)","text":""},{"location":"docs/CODE_TAG_SUMMARY/","title":"Collected Code Tags","text":"Type Comment Last Edit Source File TODO If no stale, write out five oldest? 2023-05-13 calcipy/check_for_stale_packages/_check_for_stale_packages.py:220 TODO Consider adding a configuration item for ignore_patterns 2023-02-19 calcipy/file_search.py:88 TODO Look into running tasks from within other tasks to support \u2018\u2013continue\u2019 and more readable logs \u2013> 2023-02-19 docs/docs/MIGRATION.md:112 TODO Capture logging output and check\u2026 2023-02-19 tests/check_for_stale_packages/test_check_for_stale_packages.py:64 TODO Capture logging output and check\u2026 2023-02-19 tests/check_for_stale_packages/test_check_for_stale_packages.py:88 TODO Is there an easier way to maintain pytest parameter IDs? 2023-02-17 tests/tasks/test_test.py:14 PLANNED Convert to hypothesis test! 2023-02-19 tests/test_dot_dict.py:9 Found code tags for TODO (6), PLANNED (1)"},{"location":"docs/DEVELOPER_GUIDE/","title":"Developer Notes","text":""},{"location":"docs/DEVELOPER_GUIDE/#local-development","title":"Local Development","text":"git clone https://github.com/kyleking/calcipy.git\ncd calcipy\npoetry install --sync\npoetry run calcipy-pack pack.install-extras\n\n# See the available tasks\npoetry run calcipy\n# Or use a local 'run' file (so that 'calcipy' can be extended)\n./run\n\n# Run the default task list (lint, auto-format, test coverage, etc.)\n./run main\n\n# Make code changes and run specific tasks as needed:\n./run lint.fix test\n"},{"location":"docs/DEVELOPER_GUIDE/#publishing","title":"Publishing","text":"For testing, create an account on TestPyPi. Replace ... with the API token generated on TestPyPi or PyPi respectively poetry config repositories.testpypi https://test.pypi.org/legacy/\npoetry config pypi-token.testpypi ...\n\n./run main pack.publish --to-test-pypi\n# If you didn't configure a token, you will need to provide your username and password to publish\n To publish to the real PyPi poetry config pypi-token.pypi ...\n./run release\n\n# Or for a pre-release\n./run release --suffix=rc\n"},{"location":"docs/DEVELOPER_GUIDE/#current-status","title":"Current Status","text":"File Statements Missing Excluded Coverage calcipy/__init__.py 16 0 24 100.0% calcipy/can_skip.py 17 1 0 89.3% calcipy/check_for_stale_packages/__init__.py 5 2 0 60.0% calcipy/check_for_stale_packages/_check_for_stale_packages.py 118 8 3 87.2% calcipy/cli.py 35 1 78 93.0% calcipy/code_tag_collector/__init__.py 5 2 0 60.0% calcipy/code_tag_collector/_collector.py 143 2 0 94.0% calcipy/dot_dict/__init__.py 5 2 0 60.0% calcipy/dot_dict/_dot_dict.py 8 0 0 100.0% calcipy/experiments/__init__.py 0 0 0 100.0% calcipy/experiments/bump_programmatically.py 24 24 0 0.0% calcipy/experiments/check_duplicate_test_names.py 36 0 2 95.0% calcipy/file_search.py 38 0 2 91.8% calcipy/invoke_helpers.py 30 2 0 81.8% calcipy/md_writer/__init__.py 5 2 0 60.0% calcipy/md_writer/_writer.py 95 6 0 88.9% calcipy/noxfile/__init__.py 5 2 0 60.0% calcipy/noxfile/_noxfile.py 44 2 51 83.8% calcipy/scripts.py 5 0 37 100.0% calcipy/tasks/__init__.py 0 0 0 100.0% calcipy/tasks/_invoke.py 34 0 55 97.6% calcipy/tasks/all_tasks.py 48 0 0 95.5% calcipy/tasks/cl.py 28 5 0 75.0% calcipy/tasks/defaults.py 20 0 0 89.3% calcipy/tasks/doc.py 45 0 8 90.5% calcipy/tasks/executable_utils.py 27 0 0 87.2% calcipy/tasks/lint.py 44 2 0 82.9% calcipy/tasks/nox.py 8 0 0 100.0% calcipy/tasks/pack.py 42 11 0 64.1% calcipy/tasks/stale.py 6 0 0 100.0% calcipy/tasks/tags.py 18 1 0 91.7% calcipy/tasks/test.py 45 1 2 89.2% calcipy/tasks/types.py 20 0 0 89.3% Totals 1019 76 262 86.5% Generated on: 2024-06-07"},{"location":"docs/MIGRATION/","title":"Migration Guide","text":""},{"location":"docs/MIGRATION/#calcipy-100","title":"calcipy 1.0.0","text":""},{"location":"docs/MIGRATION/#background","title":"Background","text":"calcipy v1 was a complete rewrite to switch from doit to invoke: with invoke, tasks can be run from anywhere without a dodo.py file tasks can be loaded lazily, which means that some performance gains are possible since there is no shared state file, tasks can be more easily run from pre-commit or generally in parallel doit excelled at clearly delineated task output and run summary, but invoke isn\u2019t designed that way. I would like to improve the CLI output, but the benefits are worth this tradeoff. calcipy v0 was built on doit and thus required a dodo.py file. I began adding cement to support a separate CLI for calcipy installed with pipx, but that required a lot of boilerplate code. With doit, the string command needed to be complete at task evaluation rather than runtime, so globbing files couldn\u2019t be resolved lazily."},{"location":"docs/MIGRATION/#migration","title":"Migration","text":"While refactoring, the global configuration was mostly removed (DoitGlobals) along with a few tasks, but the main functionality is still present. Any project dependent on calcipy will need substantial changes. The easiest way to start migrating is to run copier copy gh:KyleKing/calcipy_template . for calcipy_template"},{"location":"docs/MIGRATION/#speed-test","title":"Speed Test","text":"It turns out that switching to invoke appears to have only saved 100ms > hyperfine -m 20 --warmup 5 \"poetry run python -c 'print(1)'\"\nBenchmark 1: poetry run python -c 'print(1)'\nTime (mean \u00b1 \u03c3): 377.9 ms \u00b1 3.1 ms [User: 235.0 ms, System: 61.8 ms]\nRange (min \u2026 max): 372.7 ms \u2026 384.0 ms 20 runs\n> hyperfine -m 20 --warmup 5 ./run\nBenchmark 1: ./run\nTime (mean \u00b1 \u03c3): 936.0 ms \u00b1 26.9 ms [User: 1548.2 ms, System: 1687.7 ms]\nRange (min \u2026 max): 896.4 ms \u2026 1009.4 ms 20 runs\n> hyperfine -m 20 --warmup 5 \"poetry run calcipy_tags\"\nBenchmark 1: poetry run calcipy_tags\nTime (mean \u00b1 \u03c3): 618.5 ms \u00b1 29.7 ms [User: 1536.8 ms, System: 1066.2 ms]\nRange (min \u2026 max): 578.2 ms \u2026 694.9 ms 20 runs\n> hyperfine -m 20 --warmup 5 \"poetry run doit list\"\nBenchmark 1: poetry run doit list\nTime (mean \u00b1 \u03c3): 1.002 s \u00b1 0.015 s [User: 1.643 s, System: 1.682 s]\nRange (min \u2026 max): 0.974 s \u2026 1.023 s 20 runs\n Additionally, the major decrease in dependencies will make install and update actions much faster. With the recommended extras installed, calcipy-v1 has 124 dependencies (with all extras, 164) vs. calcipy-v0\u2019s 259. Counted with: cat .calcipy_packaging.lock | jq 'keys' | wc -l"},{"location":"docs/MIGRATION/#code-comparison","title":"Code Comparison","text":"Accounting for code extracted to corallium, the overall number of lines decreased from 1772 to 1550 or only 12%, while increasing the CLI and pre-commit capabilities. ~/calcipy-v0 > cloc calcipy\n-------------------------------------------------------------------------------\nLanguage files blank comment code\n-------------------------------------------------------------------------------\nPython 26 942 1075 1772\n-------------------------------------------------------------------------------\nSUM: 26 942 1075 1772\n-------------------------------------------------------------------------------\n~/calcipy > cloc calcipy\n-------------------------------------------------------------------------------\nLanguage files blank comment code\n-------------------------------------------------------------------------------\nPython 27 454 438 1185\n-------------------------------------------------------------------------------\nSUM: 27 454 438 1185\n-------------------------------------------------------------------------------\n~/corallium > cloc corallium\n-------------------------------------------------------------------------------\nLanguage files blank comment code\n-------------------------------------------------------------------------------\nPython 7 176 149 365\n-------------------------------------------------------------------------------\nSUM: 7 176 149 365\n-------------------------------------------------------------------------------\n\n~/calcipy > cloc tests\n-------------------------------------------------------------------------------\nLanguage files blank comment code\n-------------------------------------------------------------------------------\nYAML 2 0 0 580\nPython 19 176 68 578\nJSON 2 0 0 60\nMarkdown 3 9 10 8\nText 1 0 0 2\n-------------------------------------------------------------------------------\nSUM: 27 185 78 1228\n-------------------------------------------------------------------------------\n~/calcipy-v0 > cloc tests\n-------------------------------------------------------------------------------\nLanguage files blank comment code\n-------------------------------------------------------------------------------\nJSON 30 0 0 762\nYAML 2 0 0 580\nPython 24 314 186 578\nMarkdown 3 9 10 8\n-------------------------------------------------------------------------------\nSUM: 59 323 196 1928\n-------------------------------------------------------------------------------\n~/corallium > cloc tests\n-------------------------------------------------------------------------------\nLanguage files blank comment code\n-------------------------------------------------------------------------------\nPython 6 36 15 69\nMarkdown 1 1 0 2\n-------------------------------------------------------------------------------\nSUM: 7 37 15 71\n-------------------------------------------------------------------------------\n"},{"location":"docs/MIGRATION/#doit-output","title":"doit output","text":"I would like to restore the doit task summary, but invoke\u2019s architecture doesn\u2019t really make this possible. The --continue option was extremely useful, but that also might not be achievable. > poetry run doit run\n. format_recipes > [\n Python: function format_recipes\n]\n\n2023-02-19 10:40:23.954 | INFO | recipes.formatter:_write_toc:287 - Creating TOC for: ./recipes/docs/breakfast\n2023-02-19 10:40:23.957 | INFO | recipes.formatter:_write_toc:287 - Creating TOC for: ./recipes/docs/rice\n2023-02-19 10:40:23.959 | INFO | recipes.formatter:_write_toc:287 - Creating TOC for: ./recipes/docs/meals\n2023-02-19 10:40:23.964 | INFO | recipes.formatter:_write_toc:287 - Creating TOC for: ./recipes/docs/seafood\n2023-02-19 10:40:23.967 | INFO | recipes.formatter:_write_toc:287 - Creating TOC for: ./recipes/docs/pizza\n2023-02-19 10:40:23.969 | INFO | recipes.formatter:_write_toc:287 - Creating TOC for: ./recipes/docs/poultry\n2023-02-19 10:40:23.972 | INFO | recipes.formatter:_write_toc:287 - Creating TOC for: ./recipes/docs/sushi\n. collect_code_tags > [\n Python: function write_code_tag_file\n]\n\n. cl_write > [\n Cmd: poetry run cz changelog\n Python: function _move_cl\n]\n\n. lock > [\n Cmd: poetry lock --no-update\n]\n\nResolving dependencies...\n. nox_coverage > [\n Cmd: poetry run nox --error-on-missing-interpreters --session coverage\n]\n\n...\n\ndoit> Summary:\ndoit> format_recipes was successful\ndoit> collect_code_tags was successful\ndoit> cl_write was successful\ndoit> lock was successful\ndoit> nox_coverage was successful\ndoit> auto_format was successful\ndoit> document was successful\ndoit> check_for_stale_packages was successful\ndoit> pre_commit_hooks failed (red)\ndoit> lint_project was not run\ndoit> static_checks was not run\ndoit> security_checks was not run\ndoit> check_types was not run\n"},{"location":"docs/STYLE_GUIDE/","title":"Personal Style Guides","text":""},{"location":"docs/STYLE_GUIDE/#git","title":"Git","text":"We use Commitizen to manage both an auto-generated Changelog and incrementing the release version following semver. For both of these automated outputs to work well, please follow the Conventional Commits style, which is described in more detail below."},{"location":"docs/STYLE_GUIDE/#commitizen-types-and-scopes","title":"Commitizen Types and Scopes","text":"type(scope): description Types fix: A bug fix feat: A new feature docs: Documentation-only changes (code comments, separate docs) style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons) perf: A code change that improves performance refactor: A change to production code that is not a fix, feat, or perf test: Adding missing or correcting existing tests build: Changes that affect the build system or external dependencies ci: Changes to our CI configuration files and scripts A ! can be used to indicate a breaking change (refactor!: drop support for Node 6) SemVer Rules Based on commit type, the version will be auto-incremented: fix : PATCH // feat : MINOR // BREAKING CHANGE : MAJOR Scopes A Class, File name, Issue Number, other appropriate noun. As examples: build(poetry): bump requests to v3 or style(#32): add missing type annotations Tips What if a commit fits multiple types? Go back and make multiple commits whenever possible. Part of the benefit of Conventional Commits is the focus on more organized and intentional changes Use git rebase -i to fix commit names prior to merging if incorrect types/scopes are used "},{"location":"docs/STYLE_GUIDE/#git-description-guidelines","title":"Git Description Guidelines","text":" Commit message guidelines Full sentence with verb (lowercase) and concise description. Below are modified examples for Conventional Commits fix(roles): bug in admin role permissions feat(ui): implement new button design build(pip): upgrade package to remove vulnerabilities refactor: file structure to improve code readability perf(cli): rewrite methods feat(api): endpoints to implement new customer dashboard How to write a good commit message A diff will tell you what changed, but only the commit message can properly tell you why. Keep in mind: This has all been said before. From the seven rules of a great Git commit message: (2) Try for 50 characters, but consider 72 the hard limit (7) Use the body to explain what and why vs. how "},{"location":"docs/STYLE_GUIDE/#issue-labels-and-milestones","title":"Issue Labels and Milestones","text":"Personal Guide For Issue Labels, see [labels.yml][labels] Milestones Current Tasks: main milestone (name could change based on a specific project, sprint, or month) Next Tasks Blue Sky Research [Sane GitHub Labels](https://medium.com/@dave_lunny/sane-github-labels-c5d2e6004b63) and see [sensible-github-labels](https://github.com/Relequestual/sensible-github-labels) for full descriptions of each \u201cit is much more helpful to see the status and type of all issues at a glance.\u201d One of each: Status: \u2026 Abandoned, Accepted, Available, Blocked, Completed, In Progress, On Hold, Pending, Review Needed, Revision Needed Type: \u2026 Bug, Maintenance, Question, Enhancement Priority: \u2026 Critical, High, Medium, Low [Britecharts](https://britecharts.github.io/britecharts/github-labels.html) Status: \u2026 On Review \u2013 Request that we are pondering if including or not Needs Reproducing \u2013 For bugs that need to be reproduced in order to get fixed Needs Design \u2013 Feature that needs a design Ready to Go \u2013 Issue that has been defined and is ready to get started with In Progress \u2013 Issue that is being worked on right now. Completed \u2013 Finished feature or fix Type: \u2026 Bug \u2013 An unexpected problem or unintended behavior Feature \u2013 A new feature request Maintenance \u2013 A regular maintenance chore or task, including refactors, build system, CI, performance improvements Documentation \u2013 A documentation improvement task Question \u2013 An issue or PR that needs more information or a user question Not Included Priority: They would add complexity and overhead due to the discussions, but could help with the roadmap Technology Labels: It can create too much overhead, as properly tag with technologies all the issues could be time consuming [Ian Bicking Blog](https://www.ianbicking.org/blog/2014/03/use-github-issues-to-organize-a-project.html) Milestone Overview What are we doing right now? What aren\u2019t we doing right now? 2a. Stuff we\u2019ll probably do soon 2b. Stuff we probably won\u2019t do soon What aren\u2019t we sure about? Milestone Descriptions Stuff we are doing right now: this is the \u201cmain\u201d milestone. We give it a name (like Alpha 2 or Strawberry Rhubarb Pie) and we write down what we are trying to accomplish with the milestone. We create a new milestone when we are ready for the next iteration. Stuff we\u2019ll probably do soon: this is a standing \u201c**Next Tasks**\u201d milestone. We never change or rename this milestone. We use a permanent \u201cNext Tasks\u201d milestone (as opposed to renaming it to \u201cAlpha 3\u201d or actual-next-iteration milestone) because we don\u2019t want to presume or default to including something in the real next iteration. When we\u2019re ready to start planning the next iteration we\u2019ll create a new milestone, and only deliberately move things into that milestone. Stuff we probably won\u2019t do soon: this is a standing \u201c**Blue Sky**\u201d milestone. We refer to these tickets and sometimes look through them, but they are easy to ignore, somewhat intentionally ignored. What aren\u2019t we sure about?: issues with no milestone. Label: \u201cNeeds Discussion\u201d - (addressed in a triage meeting) - use liberally for either big or small tickets \u201cIt\u2019s better to give people more power: it\u2019s actually helpful if people can overreach because it is an opportunity to establish where the limits really are and what purpose those limits have\u201d "},{"location":"docs/STYLE_GUIDE/#external-links","title":"External Links","text":" [Git: The Simple Guide][simple_git] [Commit Messages][gcmsg] and why use the present tense GitHub\u2019s Advice on GitHub Most Comprehensive Guide Git Pro Book (free) Bash Tab-Completion Snippet "},{"location":"docs/STYLE_GUIDE/#python","title":"Python","text":" Python Style Guides https://gist.github.com/sloria/7001839 http://www.nilunder.com/blog/2013/08/03/pythonic-sensibilities/ https://innoq.github.io/cards42org_en/ https://docs.openstack.org/hacking/latest/user/hacking.html#styleguide https://www.python.org/doc/humor/ https://docs.python-guide.org/writing/reading/ https://realpython.com/python-refactoring/ "},{"location":"docs/STYLE_GUIDE/#adrs","title":"ADRs","text":" ADR Approaches https://infraeng.dev/tech-spec Template (And associated review) vs. https://infraeng.dev/decision-log/ Y-Statements: abbreviated shorthand. Add this as a one-line decision option if a full ADR isn\u2019t needed (or when referencing an existing ADR) (https://scribe.rip/@docsoc/y-statements-10eb07b5a177) https://adr.github.io More formal implementation of ADRs (MADR) that this is based on. Template: https://github.com/adr/madr/blob/97fb8edec60b8dc70b8166ef62de34c4e26b46c0/template/adr-template.md https://github.com/ethereum/EIPs/blob/confluenceuser/EIPS/eip-5639.md Examples https://github.com/pawamoy/mkdocstrings/issues/28 https://github.com/arachne-framework/architecture/blob/060a956277a5ad71df93da49fee52463408841af/adr-002-configuration.md https://github.com/arachne-framework/architecture/tree/060a956277a5ad71df93da49fee52463408841af https://github.com/ethereum/EIPs/blob/confluenceuser/EIPS/eip-1010.md https://docs-v1.prefect.io/core/pins/pin-01-introduce-pins.html https://peps.python.org/pep-0387/ https://github.com/AICoE/aicoe-ci/blob/39de02af86a0d1f9dcd395fa88b858f1c6880411/docs/adr/0000-use-markdown-architectural-decision-records.md And many others! <\u2013 Links \u2013>"},{"location":"modules/calcipy/_code_diagrams/","title":"Code Diagrams","text":"Auto-generated with pylint-pyreverse"},{"location":"modules/calcipy/_code_diagrams/#packages","title":"Packages","text":"Full Size"},{"location":"modules/calcipy/_code_diagrams/#classes","title":"Classes","text":"Full Size"},{"location":"reference/SUMMARY/","title":"SUMMARY","text":" calcipy can_skip check_for_stale_packages _check_for_stale_packages cli code_tag_collector _collector dot_dict _dot_dict experiments bump_programmatically check_duplicate_test_names file_search invoke_helpers md_writer _writer noxfile _noxfile scripts tasks _invoke all_tasks cl defaults doc executable_utils lint nox pack stale tags test types "},{"location":"reference/calcipy/","title":"calcipy","text":"calcipy."},{"location":"reference/calcipy/#calcipy-functions","title":"Functions","text":""},{"location":"reference/calcipy/#calcipy.configure_runtime_type_checking_mode","title":"configure_runtime_type_checking_mode","text":"configure_runtime_type_checking_mode()\n Optionally configure runtime type checking mode globally. Source code in calcipy/__init__.py def configure_runtime_type_checking_mode() -> None: # pragma: no cover\n \"\"\"Optionally configure runtime type checking mode globally.\"\"\"\n rtc_mode = _RuntimeTypeCheckingModes.from_environment()\n\n if rtc_mode is not _RuntimeTypeCheckingModes.OFF:\n from beartype.roar import BeartypeClawDecorWarning # noqa: PLC0415\n\n beartype_this_package(conf=BeartypeConf(\n warning_cls_on_decorator_exception=(\n None if rtc_mode is _RuntimeTypeCheckingModes.ERROR else BeartypeClawDecorWarning\n ),\n ))\n"},{"location":"reference/calcipy/can_skip/","title":"can_skip","text":"Support can-skip logic from Make."},{"location":"reference/calcipy/can_skip/#calcipy.can_skip-functions","title":"Functions","text":""},{"location":"reference/calcipy/can_skip/#calcipy.can_skip.can_skip","title":"can_skip","text":"can_skip(*, prerequisites, targets)\n Return true if the prerequisite files are have newer mtime than targets. Example use with Invoke, but can be used anywhere: @task()\ndef test(ctx: Context) -> None:\n if can_skip(prerequisites=[*Path('src').rglob('*.py')], targets=[Path('.coverage.xml')]):\n return # Exit early\n\n ... # Task code\n Source code in calcipy/can_skip.py @beartype\ndef can_skip(*, prerequisites: List[Path], targets: List[Path]) -> bool:\n \"\"\"Return true if the prerequisite files are have newer `mtime` than targets.\n\n Example use with Invoke, but can be used anywhere:\n\n ```py\n @task()\n def test(ctx: Context) -> None:\n if can_skip(prerequisites=[*Path('src').rglob('*.py')], targets=[Path('.coverage.xml')]):\n return # Exit early\n\n ... # Task code\n ```\n\n \"\"\"\n if not (ts_prerequisites := [pth.stat().st_mtime for pth in prerequisites]):\n raise ValueError('Required files do not exist', prerequisites)\n\n ts_targets = [pth.stat().st_mtime for pth in targets]\n if ts_targets and min(ts_targets) > max(ts_prerequisites):\n LOGGER.warning('Skipping because targets are newer', targets=targets)\n return True\n return False\n"},{"location":"reference/calcipy/can_skip/#calcipy.can_skip.dont_skip","title":"dont_skip","text":"dont_skip(*, prerequisites, targets)\n To use for testing with mock; always returns False. Source code in calcipy/can_skip.py @beartype\ndef dont_skip(*, prerequisites: List[Path], targets: List[Path]) -> bool:\n \"\"\"To use for testing with mock; always returns False.\"\"\"\n LOGGER.debug('Mocking can_skip', prerequisites=prerequisites, targets=targets)\n return False\n"},{"location":"reference/calcipy/cli/","title":"cli","text":"Extend Invoke for Calcipy."},{"location":"reference/calcipy/cli/#calcipy.cli-classes","title":"Classes","text":""},{"location":"reference/calcipy/cli/#calcipy.cli.CalcipyConfig","title":"CalcipyConfig","text":" Bases: Config Opinionated Config with better defaults. Source code in calcipy/cli.py class CalcipyConfig(Config):\n \"\"\"Opinionated Config with better defaults.\"\"\"\n\n @staticmethod\n def global_defaults() -> Dict: # type: ignore[type-arg] # pragma: no cover\n \"\"\"Override the global defaults.\"\"\"\n invoke_defaults = Config.global_defaults()\n calcipy_defaults = {\n 'run': {\n 'echo': True,\n 'echo_format': '\\033[2;3;37mRunning: {command}\\033[0m',\n 'pty': use_pty(),\n },\n }\n return merge_dicts(invoke_defaults, calcipy_defaults)\n"},{"location":"reference/calcipy/cli/#calcipy.cli.CalcipyConfig-functions","title":"Functions","text":""},{"location":"reference/calcipy/cli/#calcipy.cli.CalcipyConfig.global_defaults","title":"global_defaults staticmethod","text":"global_defaults()\n Override the global defaults. Source code in calcipy/cli.py @staticmethod\ndef global_defaults() -> Dict: # type: ignore[type-arg] # pragma: no cover\n \"\"\"Override the global defaults.\"\"\"\n invoke_defaults = Config.global_defaults()\n calcipy_defaults = {\n 'run': {\n 'echo': True,\n 'echo_format': '\\033[2;3;37mRunning: {command}\\033[0m',\n 'pty': use_pty(),\n },\n }\n return merge_dicts(invoke_defaults, calcipy_defaults)\n"},{"location":"reference/calcipy/cli/#calcipy.cli-functions","title":"Functions","text":""},{"location":"reference/calcipy/cli/#calcipy.cli.start_program","title":"start_program","text":"start_program(pkg_name, pkg_version, module=None, collection=None)\n Run the customized Invoke Program. FYI: recommendation is to extend the core_args method, but this won\u2019t parse positional arguments: https://docs.pyinvoke.org/en/stable/concepts/library.html#modifying-core-parser-arguments Source code in calcipy/cli.py @beartype\ndef start_program(\n pkg_name: str,\n pkg_version: str,\n module: Optional[ModuleType] = None,\n collection: Optional[Union[Collection, InvokeCollection]] = None,\n) -> None: # pragma: no cover\n \"\"\"Run the customized Invoke Program.\n\n FYI: recommendation is to extend the `core_args` method, but this won't parse positional arguments:\n https://docs.pyinvoke.org/en/stable/concepts/library.html#modifying-core-parser-arguments\n\n \"\"\"\n # Manipulate 'sys.argv' to hide arguments that invoke can't parse\n _gto = GlobalTaskOptions()\n sys_argv: List[str] = sys.argv[:1]\n last_argv = ''\n for argv in sys.argv[1:]:\n if not last_argv.startswith('-') and Path(argv).is_file():\n _gto.file_args.append(Path(argv))\n # Check for CLI flags\n elif argv in {'-v', '-vv', '-vvv', '--verbose'}:\n _gto.verbose = argv.count('v')\n elif argv == '--keep-going':\n _gto.keep_going = True\n # Check for CLI arguments with values\n elif last_argv == '--working-dir':\n _gto.working_dir = Path(argv).resolve()\n elif argv != '--working-dir':\n sys_argv.append(argv)\n last_argv = argv\n _gto.file_args = [\n _f if _f.is_absolute() else Path.cwd() / _f\n for _f in _gto.file_args\n ]\n sys.argv = sys_argv\n\n class _CalcipyConfig(CalcipyConfig):\n\n gto: GlobalTaskOptions = _gto\n\n if module and collection:\n raise ValueError('Only one of collection or module can be specified')\n\n _CalcipyProgram(\n name=pkg_name,\n version=pkg_version,\n namespace=Collection.from_module(module) if module else collection,\n config_class=_CalcipyConfig,\n ).run()\n"},{"location":"reference/calcipy/cli/#calcipy.cli.task","title":"task","text":"task(*dec_args, **dec_kwargs)\n Mark wrapped callable object as a valid Invoke task. Source code in calcipy/cli.py def task(*dec_args: Any, **dec_kwargs: Any) -> Callable: # type: ignore[type-arg]\n \"\"\"Mark wrapped callable object as a valid Invoke task.\"\"\"\n def wrapper(func: Any) -> Callable: # type: ignore[type-arg]\n # Attach arguments for Task\n setattr(func, TASK_ARGS_ATTR, dec_args)\n setattr(func, TASK_KWARGS_ATTR, dec_kwargs)\n # Attach public attributes from invoke that are expected\n func.help = dec_kwargs.pop('help', {})\n\n def _with_kwargs(**extra_kwargs: Any) -> Callable: # type: ignore[type-arg] # nosem\n \"\"\"Support building partial tasks.\"\"\"\n if extra_kwargs:\n # Set a unique name when 'extra_kwargs' was provided\n # https://github.com/pyinvoke/invoke/blob/07b836f2663bb073a7bcef3d6c454e1dc6b867ae/invoke/tasks.py#L81-L104\n encoded = b64encode(str(extra_kwargs).encode())\n func.__name__ = f'{func.__name__}_{encoded.decode().rstrip(\"=\")}'\n\n @wraps(func) # nosem\n def _with_kwargs_inner(*args: Any, **kwargs: Any) -> Any:\n return func(*args, **kwargs, **extra_kwargs)\n return _with_kwargs_inner\n\n func.with_kwargs = _with_kwargs\n\n @wraps(func) # nosem\n def _inner(*args: Any, **kwargs: Any) -> Any:\n return func(*args, **kwargs)\n\n return _inner\n\n # Handle the case when the decorator is called without arguments\n if (\n len(dec_args) == 1\n and callable(dec_args[0])\n and not hasattr(dec_args[0], TASK_ARGS_ATTR)\n ):\n return wrapper(dec_args[0])\n\n return wrapper\n"},{"location":"reference/calcipy/file_search/","title":"file_search","text":"Find Files."},{"location":"reference/calcipy/file_search/#calcipy.file_search-functions","title":"Functions","text":""},{"location":"reference/calcipy/file_search/#calcipy.file_search.find_project_files","title":"find_project_files","text":"find_project_files(path_project, ignore_patterns)\n Find project files in git version control. Note: uses the relative project directory and verifies that each file exists path_project: Path to the project directory\nignore_patterns: glob ignore patterns\n Dict[str, List[Path]]: where keys are the suffix (without leading dot) and values the list of paths\n Source code in calcipy/file_search.py @beartype\ndef find_project_files(path_project: Path, ignore_patterns: List[str]) -> List[Path]:\n \"\"\"Find project files in git version control.\n\n > Note: uses the relative project directory and verifies that each file exists\n\n Args:\n ----\n path_project: Path to the project directory\n ignore_patterns: glob ignore patterns\n\n Returns:\n -------\n Dict[str, List[Path]]: where keys are the suffix (without leading dot) and values the list of paths\n\n \"\"\"\n file_paths = []\n rel_filepaths = _get_all_files(cwd=path_project)\n filtered_rel_files = _filter_files(rel_filepaths=rel_filepaths, ignore_patterns=ignore_patterns)\n for rel_file in filtered_rel_files:\n path_file = path_project / rel_file\n if path_file.is_file():\n file_paths.append(path_file)\n else: # pragma: no cover\n LOGGER.warning('Could not find the specified file', path_file=path_file)\n return file_paths\n"},{"location":"reference/calcipy/file_search/#calcipy.file_search.find_project_files_by_suffix","title":"find_project_files_by_suffix","text":"find_project_files_by_suffix(path_project, *, ignore_patterns=None)\n Find project files in git version control. Note: uses the relative project directory and verifies that each file exists path_project: Path to the project directory\nignore_patterns: glob ignore patterns\n Dict[str, List[Path]]: where keys are the suffix (without leading dot) and values the list of paths\n Source code in calcipy/file_search.py @beartype\ndef find_project_files_by_suffix(\n path_project: Path, *, ignore_patterns: Optional[List[str]] = None,\n) -> Dict[str, List[Path]]:\n \"\"\"Find project files in git version control.\n\n > Note: uses the relative project directory and verifies that each file exists\n\n Args:\n ----\n path_project: Path to the project directory\n ignore_patterns: glob ignore patterns\n\n Returns:\n -------\n Dict[str, List[Path]]: where keys are the suffix (without leading dot) and values the list of paths\n\n \"\"\"\n file_lookup: Dict[str, List[Path]] = defaultdict(list)\n for path_file in find_project_files(path_project, ignore_patterns or []):\n file_lookup[path_file.suffix.lstrip('.')].append(path_file)\n return dict(file_lookup)\n"},{"location":"reference/calcipy/invoke_helpers/","title":"invoke_helpers","text":"Invoke Helpers."},{"location":"reference/calcipy/invoke_helpers/#calcipy.invoke_helpers-functions","title":"Functions","text":""},{"location":"reference/calcipy/invoke_helpers/#calcipy.invoke_helpers.get_doc_subdir","title":"get_doc_subdir","text":"get_doc_subdir(path_project=None)\n Retrieve the documentation directory from the copier answer file. path_project: Path to the project directory with contains `.copier-answers.yml`\n Path: to the source documentation directory\n Source code in calcipy/invoke_helpers.py @beartype\ndef get_doc_subdir(path_project: Optional[Path] = None) -> Path:\n \"\"\"Retrieve the documentation directory from the copier answer file.\n\n Args:\n ----\n path_project: Path to the project directory with contains `.copier-answers.yml`\n\n Returns:\n -------\n Path: to the source documentation directory\n\n \"\"\"\n path_copier = (path_project or get_project_path()) / COPIER_ANSWERS\n doc_dir = read_yaml_file(path_copier).get('doc_dir', 'docs')\n return path_copier.parent / doc_dir / 'docs'\n"},{"location":"reference/calcipy/invoke_helpers/#calcipy.invoke_helpers.get_project_path","title":"get_project_path cached","text":"get_project_path()\n Retrieve the cwd. Source code in calcipy/invoke_helpers.py @lru_cache(maxsize=1)\ndef get_project_path() -> Path:\n \"\"\"Retrieve the `cwd`.\"\"\"\n return Path.cwd()\n"},{"location":"reference/calcipy/invoke_helpers/#calcipy.invoke_helpers.run","title":"run","text":"run(ctx, *run_args, **run_kwargs)\n Wrap invoke.run to run within the working_dir. Source code in calcipy/invoke_helpers.py @beartype\ndef run(ctx: Context, *run_args: Any, **run_kwargs: Any) -> Optional[Result]:\n \"\"\"Wrap invoke.run to run within the `working_dir`.\"\"\"\n working_dir = '.'\n with suppress(AttributeError):\n working_dir = ctx.config.gto.working_dir\n\n with ctx.cd(working_dir):\n return ctx.run(*run_args, **run_kwargs)\n"},{"location":"reference/calcipy/invoke_helpers/#calcipy.invoke_helpers.use_pty","title":"use_pty cached","text":"use_pty()\n Return False on Windows and some CI environments. Source code in calcipy/invoke_helpers.py @lru_cache(maxsize=1)\ndef use_pty() -> bool:\n \"\"\"Return False on Windows and some CI environments.\"\"\"\n if platform.system() == 'Windows':\n return False\n return not environ.get('GITHUB_ACTION')\n"},{"location":"reference/calcipy/scripts/","title":"scripts","text":"Start the command line program."},{"location":"reference/calcipy/scripts/#calcipy.scripts-classes","title":"Classes","text":""},{"location":"reference/calcipy/scripts/#calcipy.scripts-functions","title":"Functions","text":""},{"location":"reference/calcipy/scripts/#calcipy.scripts.start","title":"start","text":"start()\n Run the customized Invoke Program. Source code in calcipy/scripts.py def start() -> None: # pragma: no cover\n \"\"\"Run the customized Invoke Program.\"\"\"\n from .tasks import all_tasks # noqa: PLC0415\n start_program(__pkg_name__, __version__, all_tasks)\n"},{"location":"reference/calcipy/scripts/#calcipy.scripts.start_docs","title":"start_docs","text":"start_docs()\n Run CLI with only the cl and doc namespaces. Source code in calcipy/scripts.py def start_docs() -> None: # pragma: no cover\n \"\"\"Run CLI with only the cl and doc namespaces.\"\"\"\n from .tasks import cl, doc # noqa: PLC0415\n _start_subset([cl, doc])\n"},{"location":"reference/calcipy/scripts/#calcipy.scripts.start_lint","title":"start_lint","text":"start_lint()\n Run CLI with only the lint namespace. Source code in calcipy/scripts.py def start_lint() -> None: # pragma: no cover\n \"\"\"Run CLI with only the lint namespace.\"\"\"\n from .tasks import lint # noqa: PLC0415\n _start_subset([lint])\n"},{"location":"reference/calcipy/scripts/#calcipy.scripts.start_pack","title":"start_pack","text":"start_pack()\n Run CLI with only the pack namespace. Source code in calcipy/scripts.py def start_pack() -> None: # pragma: no cover\n \"\"\"Run CLI with only the pack namespace.\"\"\"\n from .tasks import pack # noqa: PLC0415\n _start_subset([pack])\n"},{"location":"reference/calcipy/scripts/#calcipy.scripts.start_tags","title":"start_tags","text":"start_tags()\n Run CLI with only the tags namespace. Source code in calcipy/scripts.py def start_tags() -> None: # pragma: no cover\n \"\"\"Run CLI with only the tags namespace.\"\"\"\n from .tasks import tags # noqa: PLC0415\n _start_subset([tags])\n"},{"location":"reference/calcipy/scripts/#calcipy.scripts.start_test","title":"start_test","text":"start_test()\n Run CLI with only the test namespace. Source code in calcipy/scripts.py def start_test() -> None: # pragma: no cover\n \"\"\"Run CLI with only the test namespace.\"\"\"\n from .tasks import test # noqa: PLC0415\n _start_subset([test])\n"},{"location":"reference/calcipy/scripts/#calcipy.scripts.start_types","title":"start_types","text":"start_types()\n Run CLI with only the types namespace. Source code in calcipy/scripts.py def start_types() -> None: # pragma: no cover\n \"\"\"Run CLI with only the types namespace.\"\"\"\n from .tasks import types # noqa: PLC0415\n _start_subset([types])\n"},{"location":"reference/calcipy/check_for_stale_packages/","title":"check_for_stale_packages","text":""},{"location":"reference/calcipy/check_for_stale_packages/#calcipy.check_for_stale_packages-functions","title":"Functions","text":""},{"location":"reference/calcipy/check_for_stale_packages/#calcipy.check_for_stale_packages.check_for_stale_packages","title":"check_for_stale_packages","text":"check_for_stale_packages(*, stale_months, path_lock=LOCK, path_cache=CALCIPY_CACHE)\n Check for stale packages by reading from the cache, and updating if necessary. stale_months: cutoff in months for when a package might be stale enough to be a risk\npath_lock: path to poetry lock file\npath_cache: path to calcipy package cache file\n Source code in calcipy/check_for_stale_packages/_check_for_stale_packages.py @beartype\ndef check_for_stale_packages(*, stale_months: int, path_lock: Path = LOCK, path_cache: Path = CALCIPY_CACHE) -> bool:\n \"\"\"Check for stale packages by reading from the cache, and updating if necessary.\n\n Args:\n ----\n stale_months: cutoff in months for when a package might be stale enough to be a risk\n path_lock: path to poetry lock file\n path_cache: path to calcipy package cache file\n\n \"\"\"\n packages = _read_packages(path_lock)\n cached_packages = _read_cache(path_cache)\n if cached_packages and can_skip.can_skip(prerequisites=[path_lock], targets=[path_cache]):\n packages = [*cached_packages.values()]\n else:\n packages = _collect_release_dates(packages, cached_packages)\n _write_cache(packages, path_cache)\n return _packages_are_stale(packages, stale_months=stale_months)\n"},{"location":"reference/calcipy/check_for_stale_packages/_check_for_stale_packages/","title":"_check_for_stale_packages","text":"Check for stale packages."},{"location":"reference/calcipy/check_for_stale_packages/_check_for_stale_packages/#calcipy.check_for_stale_packages._check_for_stale_packages-attributes","title":"Attributes","text":""},{"location":"reference/calcipy/check_for_stale_packages/_check_for_stale_packages/#calcipy.check_for_stale_packages._check_for_stale_packages.CALCIPY_CACHE","title":"CALCIPY_CACHE module-attribute","text":"CALCIPY_CACHE = Path('.calcipy_packaging.lock')\n Path to the packaging lock file."},{"location":"reference/calcipy/check_for_stale_packages/_check_for_stale_packages/#calcipy.check_for_stale_packages._check_for_stale_packages-functions","title":"Functions","text":""},{"location":"reference/calcipy/check_for_stale_packages/_check_for_stale_packages/#calcipy.check_for_stale_packages._check_for_stale_packages.check_for_stale_packages","title":"check_for_stale_packages","text":"check_for_stale_packages(*, stale_months, path_lock=LOCK, path_cache=CALCIPY_CACHE)\n Check for stale packages by reading from the cache, and updating if necessary. stale_months: cutoff in months for when a package might be stale enough to be a risk\npath_lock: path to poetry lock file\npath_cache: path to calcipy package cache file\n Source code in calcipy/check_for_stale_packages/_check_for_stale_packages.py @beartype\ndef check_for_stale_packages(*, stale_months: int, path_lock: Path = LOCK, path_cache: Path = CALCIPY_CACHE) -> bool:\n \"\"\"Check for stale packages by reading from the cache, and updating if necessary.\n\n Args:\n ----\n stale_months: cutoff in months for when a package might be stale enough to be a risk\n path_lock: path to poetry lock file\n path_cache: path to calcipy package cache file\n\n \"\"\"\n packages = _read_packages(path_lock)\n cached_packages = _read_cache(path_cache)\n if cached_packages and can_skip.can_skip(prerequisites=[path_lock], targets=[path_cache]):\n packages = [*cached_packages.values()]\n else:\n packages = _collect_release_dates(packages, cached_packages)\n _write_cache(packages, path_cache)\n return _packages_are_stale(packages, stale_months=stale_months)\n"},{"location":"reference/calcipy/code_tag_collector/","title":"code_tag_collector","text":""},{"location":"reference/calcipy/code_tag_collector/#calcipy.code_tag_collector-functions","title":"Functions","text":""},{"location":"reference/calcipy/code_tag_collector/#calcipy.code_tag_collector.write_code_tag_file","title":"write_code_tag_file","text":"write_code_tag_file(\n *,\n path_tag_summary,\n paths_source,\n base_dir,\n regex=\"\",\n tags=\"\",\n header=\"# Task Summary\\n\\nAuto-Generated by `calcipy`\"\n)\n Create the code tag summary file. path_tag_summary: Path to the output file\npaths_source: list of source files to parse\nbase_dir: base directory relative to the searched files\nregex: compiled regular expression. Expected to have matching groups `(tag, text)`.\n Default is CODE_TAG_RE with tags from tag_order\ntags: subset of all tags to include in the report and specified order. Default is COMMON_CODE_TAGS\nheader: header text\n Source code in calcipy/code_tag_collector/_collector.py @beartype\ndef write_code_tag_file(\n *,\n path_tag_summary: Path,\n paths_source: List[Path],\n base_dir: Path,\n regex: str = '',\n tags: str = '',\n header: str = '# Task Summary\\n\\nAuto-Generated by `calcipy`',\n) -> None:\n \"\"\"Create the code tag summary file.\n\n Args:\n ----\n path_tag_summary: Path to the output file\n paths_source: list of source files to parse\n base_dir: base directory relative to the searched files\n regex: compiled regular expression. Expected to have matching groups `(tag, text)`.\n Default is CODE_TAG_RE with tags from tag_order\n tags: subset of all tags to include in the report and specified order. Default is COMMON_CODE_TAGS\n header: header text\n\n \"\"\"\n tag_order = [_t.strip() for _t in tags.split(',') if _t] or COMMON_CODE_TAGS\n matcher = (regex or CODE_TAG_RE).format(tag='|'.join(tag_order))\n\n matches = _search_files(paths_source, re.compile(matcher))\n if report := _format_report(\n base_dir, matches, tag_order=tag_order,\n ).strip():\n path_tag_summary.parent.mkdir(exist_ok=True, parents=True)\n path_tag_summary.write_text(f'{header}\\n\\n{report}\\n\\n<!-- {SKIP_PHRASE} -->\\n')\n LOGGER.text('Created Code Tag Summary', path_tag_summary=path_tag_summary)\n elif path_tag_summary.is_file():\n path_tag_summary.unlink()\n"},{"location":"reference/calcipy/code_tag_collector/_collector/","title":"_collector","text":"Collect code tags and output for review in a single location."},{"location":"reference/calcipy/code_tag_collector/_collector/#calcipy.code_tag_collector._collector-attributes","title":"Attributes","text":""},{"location":"reference/calcipy/code_tag_collector/_collector/#calcipy.code_tag_collector._collector.CODE_TAG_RE","title":"CODE_TAG_RE module-attribute","text":"CODE_TAG_RE = '((^|\\\\s|\\\\(|\"|\\\\\\')(?P<tag>{tag})(:| -)([^\\\\r\\\\n]))(?P<text>.+)'\n Default code tag regex with tag and text matching groups. Requires formatting with list of tags: CODE_TAG_RE.format(tag='|'.join(tag_list)) Commonly, the tag_list could be COMMON_CODE_TAGS"},{"location":"reference/calcipy/code_tag_collector/_collector/#calcipy.code_tag_collector._collector.COMMON_CODE_TAGS","title":"COMMON_CODE_TAGS module-attribute","text":"COMMON_CODE_TAGS = ['FIXME', 'TODO', 'PLANNED', 'HACK', 'REVIEW', 'TBD', 'DEBUG']\n Most common code tags. FYI and NOTE are excluded to not be tracked in the Code Summary."},{"location":"reference/calcipy/code_tag_collector/_collector/#calcipy.code_tag_collector._collector.SKIP_PHRASE","title":"SKIP_PHRASE module-attribute","text":"SKIP_PHRASE = 'calcipy_skip_tags'\n String that indicates the file should be excluded from the tag search."},{"location":"reference/calcipy/code_tag_collector/_collector/#calcipy.code_tag_collector._collector-functions","title":"Functions","text":""},{"location":"reference/calcipy/code_tag_collector/_collector/#calcipy.code_tag_collector._collector.github_blame_url","title":"github_blame_url","text":"github_blame_url(clone_uri)\n Format the blame URL. clone_uri: git remote URI\n str: repo_url Source code in calcipy/code_tag_collector/_collector.py @beartype\ndef github_blame_url(clone_uri: str) -> str:\n \"\"\"Format the blame URL.\n\n Args:\n ----\n clone_uri: git remote URI\n\n Returns:\n -------\n str: `repo_url`\n\n \"\"\"\n # Could be ssh or http (with or without .git)\n # > git@github.com:KyleKing/calcipy.git\n # > https://github.com/KyleKing/calcipy.git\n if matches := re.compile(_GITHUB_ORIGIN).match(clone_uri):\n github_url = 'https://github.com/'\n return f\"{github_url}{matches['owner']}/{matches['repository']}\"\n return ''\n"},{"location":"reference/calcipy/code_tag_collector/_collector/#calcipy.code_tag_collector._collector.write_code_tag_file","title":"write_code_tag_file","text":"write_code_tag_file(\n *,\n path_tag_summary,\n paths_source,\n base_dir,\n regex=\"\",\n tags=\"\",\n header=\"# Task Summary\\n\\nAuto-Generated by `calcipy`\"\n)\n Create the code tag summary file. path_tag_summary: Path to the output file\npaths_source: list of source files to parse\nbase_dir: base directory relative to the searched files\nregex: compiled regular expression. Expected to have matching groups `(tag, text)`.\n Default is CODE_TAG_RE with tags from tag_order\ntags: subset of all tags to include in the report and specified order. Default is COMMON_CODE_TAGS\nheader: header text\n Source code in calcipy/code_tag_collector/_collector.py @beartype\ndef write_code_tag_file(\n *,\n path_tag_summary: Path,\n paths_source: List[Path],\n base_dir: Path,\n regex: str = '',\n tags: str = '',\n header: str = '# Task Summary\\n\\nAuto-Generated by `calcipy`',\n) -> None:\n \"\"\"Create the code tag summary file.\n\n Args:\n ----\n path_tag_summary: Path to the output file\n paths_source: list of source files to parse\n base_dir: base directory relative to the searched files\n regex: compiled regular expression. Expected to have matching groups `(tag, text)`.\n Default is CODE_TAG_RE with tags from tag_order\n tags: subset of all tags to include in the report and specified order. Default is COMMON_CODE_TAGS\n header: header text\n\n \"\"\"\n tag_order = [_t.strip() for _t in tags.split(',') if _t] or COMMON_CODE_TAGS\n matcher = (regex or CODE_TAG_RE).format(tag='|'.join(tag_order))\n\n matches = _search_files(paths_source, re.compile(matcher))\n if report := _format_report(\n base_dir, matches, tag_order=tag_order,\n ).strip():\n path_tag_summary.parent.mkdir(exist_ok=True, parents=True)\n path_tag_summary.write_text(f'{header}\\n\\n{report}\\n\\n<!-- {SKIP_PHRASE} -->\\n')\n LOGGER.text('Created Code Tag Summary', path_tag_summary=path_tag_summary)\n elif path_tag_summary.is_file():\n path_tag_summary.unlink()\n"},{"location":"reference/calcipy/dot_dict/","title":"dot_dict","text":""},{"location":"reference/calcipy/dot_dict/#calcipy.dot_dict-functions","title":"Functions","text":""},{"location":"reference/calcipy/dot_dict/#calcipy.dot_dict.ddict","title":"ddict","text":"ddict(**kwargs)\n Return a dotted dictionary that can also be accessed normally. Currently uses python-box Could consider cleverdict which had updates as recently as 2022 There are numerous other variations that haven\u2019t been updated since 2020, such as munch, bunch, ddict **kwargs: keyword arguments formatted into dictionary\n DdictType: dotted dictionary\n Source code in calcipy/dot_dict/_dot_dict.py @beartype\ndef ddict(**kwargs: Dict[str, Any]) -> DdictType:\n \"\"\"Return a dotted dictionary that can also be accessed normally.\n\n - Currently uses `python-box`\n - Could consider `cleverdict` which had updates as recently as 2022\n - There are numerous other variations that haven't been updated since 2020, such as `munch`, `bunch`, `ddict`\n\n Args:\n ----\n **kwargs: keyword arguments formatted into dictionary\n\n Returns:\n -------\n DdictType: dotted dictionary\n\n \"\"\"\n return Box(kwargs)\n"},{"location":"reference/calcipy/dot_dict/_dot_dict/","title":"_dot_dict","text":"Dotted dictionary for consistent interface. Consider moving to Corallium, but I don\u2019t have any uses for it yet."},{"location":"reference/calcipy/dot_dict/_dot_dict/#calcipy.dot_dict._dot_dict-attributes","title":"Attributes","text":""},{"location":"reference/calcipy/dot_dict/_dot_dict/#calcipy.dot_dict._dot_dict.DdictType","title":"DdictType module-attribute","text":"DdictType = Union[Dict[str, Any], Box]\n Return type from ddict()."},{"location":"reference/calcipy/dot_dict/_dot_dict/#calcipy.dot_dict._dot_dict-functions","title":"Functions","text":""},{"location":"reference/calcipy/dot_dict/_dot_dict/#calcipy.dot_dict._dot_dict.ddict","title":"ddict","text":"ddict(**kwargs)\n Return a dotted dictionary that can also be accessed normally. Currently uses python-box Could consider cleverdict which had updates as recently as 2022 There are numerous other variations that haven\u2019t been updated since 2020, such as munch, bunch, ddict **kwargs: keyword arguments formatted into dictionary\n DdictType: dotted dictionary\n Source code in calcipy/dot_dict/_dot_dict.py @beartype\ndef ddict(**kwargs: Dict[str, Any]) -> DdictType:\n \"\"\"Return a dotted dictionary that can also be accessed normally.\n\n - Currently uses `python-box`\n - Could consider `cleverdict` which had updates as recently as 2022\n - There are numerous other variations that haven't been updated since 2020, such as `munch`, `bunch`, `ddict`\n\n Args:\n ----\n **kwargs: keyword arguments formatted into dictionary\n\n Returns:\n -------\n DdictType: dotted dictionary\n\n \"\"\"\n return Box(kwargs)\n"},{"location":"reference/calcipy/experiments/","title":"experiments","text":""},{"location":"reference/calcipy/experiments/bump_programmatically/","title":"bump_programmatically","text":"Experiment with bumping the git tag using griffe."},{"location":"reference/calcipy/experiments/bump_programmatically/#calcipy.experiments.bump_programmatically-functions","title":"Functions","text":""},{"location":"reference/calcipy/experiments/bump_programmatically/#calcipy.experiments.bump_programmatically.bump_tag","title":"bump_tag","text":"bump_tag(*, pkg_name, tag, tag_prefix)\n Make a SemVer minor bump using griffe if there were any breaking changes. Major versions must be bumped manually Source code in calcipy/experiments/bump_programmatically.py @beartype\ndef bump_tag(*, pkg_name: str, tag: str, tag_prefix: str) -> str:\n \"\"\"Make a SemVer minor bump using `griffe` if there were any breaking changes.\n\n Major versions must be bumped manually\n\n \"\"\"\n previous = griffe.load_git(pkg_name, ref=tag)\n current = griffe.load(pkg_name)\n\n breakages = [*griffe.find_breaking_changes(previous, current)]\n for breakage in breakages:\n try:\n LOGGER.text(breakage._explain_oneline()) # noqa: SLF001\n except BuiltinModuleError: # noqa: PERF203\n LOGGER.warning(str(breakage))\n except Exception:\n LOGGER.exception(str(breakage))\n\n try:\n ver = semver.Version.parse(tag.replace(tag_prefix, ''))\n except ValueError:\n LOGGER.exception('Failed to parse tag', tag=tag)\n return ''\n new_ver = ver.bump_minor() if any(breakages) else ver.bump_patch()\n return f'{tag_prefix}{new_ver}'\n"},{"location":"reference/calcipy/experiments/check_duplicate_test_names/","title":"check_duplicate_test_names","text":"Experiment with checking for duplicate test names."},{"location":"reference/calcipy/experiments/check_duplicate_test_names/#calcipy.experiments.check_duplicate_test_names-functions","title":"Functions","text":""},{"location":"reference/calcipy/experiments/check_duplicate_test_names/#calcipy.experiments.check_duplicate_test_names.run","title":"run","text":"run(test_path)\n Check for duplicates in the test suite. Inspired by: https://stackoverflow.com/a/67840804/3219667 Source code in calcipy/experiments/check_duplicate_test_names.py @beartype\ndef run(test_path: Path) -> List[str]: # noqa: C901 # pylint: disable=too-complex\n \"\"\"Check for duplicates in the test suite.\n\n Inspired by: https://stackoverflow.com/a/67840804/3219667\n\n \"\"\"\n summary = set()\n duplicates = []\n\n for path_test in test_path.rglob('test_*.py'): # pylint: disable=too-many-nested-blocks\n LOGGER.info(path_test.as_posix())\n parsed_ast = ast.parse(path_test.read_text())\n\n for node in parsed_ast.body:\n if isinstance(node, ast.FunctionDef):\n if node.name in summary and node.name.startswith('test_'):\n duplicates.append(node.name)\n summary.add(node.name)\n _show_info(node)\n elif isinstance(node, ast.ClassDef):\n LOGGER.info('Class name', name=node.name)\n for method in node.body:\n if isinstance(method, ast.FunctionDef):\n _show_info(method)\n\n for node in ast.walk(parsed_ast): # type: ignore[assignment]\n if (\n isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))\n and node.name not in summary\n ):\n LOGGER.info('Found new function(s) through walking')\n _show_info(node)\n summary.add(node.name)\n\n if duplicates:\n LOGGER.error('Found Duplicates', duplicates=duplicates)\n return duplicates\n"},{"location":"reference/calcipy/md_writer/","title":"md_writer","text":""},{"location":"reference/calcipy/md_writer/#calcipy.md_writer-functions","title":"Functions","text":""},{"location":"reference/calcipy/md_writer/#calcipy.md_writer.write_autoformatted_md_sections","title":"write_autoformatted_md_sections","text":"write_autoformatted_md_sections(handler_lookup=None, paths_md=None)\n Populate the auto-formatted sections of markdown files with user-configured logic. Source code in calcipy/md_writer/_writer.py @beartype\ndef write_autoformatted_md_sections(\n handler_lookup: Optional[HandlerLookupT] = None,\n paths_md: Optional[List[Path]] = None,\n) -> None:\n \"\"\"Populate the auto-formatted sections of markdown files with user-configured logic.\"\"\"\n _lookup: HandlerLookupT = handler_lookup or {\n 'COVERAGE ': _handle_coverage,\n 'SOURCE_FILE=': _handle_source_file,\n }\n\n paths = paths_md or find_project_files_by_suffix(get_project_path()).get('md') or []\n for path_md in paths:\n LOGGER.text_debug('Processing', path_md=path_md)\n if md_lines := _ReplacementMachine().parse(read_lines(path_md), _lookup, path_md):\n path_md.write_text('\\n'.join(md_lines) + '\\n', encoding='utf-8')\n"},{"location":"reference/calcipy/md_writer/_writer/","title":"_writer","text":"Markdown Machine."},{"location":"reference/calcipy/md_writer/_writer/#calcipy.md_writer._writer-attributes","title":"Attributes","text":""},{"location":"reference/calcipy/md_writer/_writer/#calcipy.md_writer._writer.HandlerLookupT","title":"HandlerLookupT module-attribute","text":"HandlerLookupT = Dict[str, Callable[[str, Path], List[str]]]\n Handler Lookup."},{"location":"reference/calcipy/md_writer/_writer/#calcipy.md_writer._writer-functions","title":"Functions","text":""},{"location":"reference/calcipy/md_writer/_writer/#calcipy.md_writer._writer.write_autoformatted_md_sections","title":"write_autoformatted_md_sections","text":"write_autoformatted_md_sections(handler_lookup=None, paths_md=None)\n Populate the auto-formatted sections of markdown files with user-configured logic. Source code in calcipy/md_writer/_writer.py @beartype\ndef write_autoformatted_md_sections(\n handler_lookup: Optional[HandlerLookupT] = None,\n paths_md: Optional[List[Path]] = None,\n) -> None:\n \"\"\"Populate the auto-formatted sections of markdown files with user-configured logic.\"\"\"\n _lookup: HandlerLookupT = handler_lookup or {\n 'COVERAGE ': _handle_coverage,\n 'SOURCE_FILE=': _handle_source_file,\n }\n\n paths = paths_md or find_project_files_by_suffix(get_project_path()).get('md') or []\n for path_md in paths:\n LOGGER.text_debug('Processing', path_md=path_md)\n if md_lines := _ReplacementMachine().parse(read_lines(path_md), _lookup, path_md):\n path_md.write_text('\\n'.join(md_lines) + '\\n', encoding='utf-8')\n"},{"location":"reference/calcipy/noxfile/","title":"noxfile","text":""},{"location":"reference/calcipy/noxfile/#calcipy.noxfile-functions","title":"Functions","text":""},{"location":"reference/calcipy/noxfile/#calcipy.noxfile.build_check","title":"build_check","text":"build_check(session)\n Check that the built output meets all checks. Source code in calcipy/noxfile/_noxfile.py @nox_poetry_session(python=_get_pythons()[-1:], reuse_venv=True)\ndef build_check(session: NPSession) -> None: # pragma: no cover\n \"\"\"Check that the built output meets all checks.\"\"\"\n # Build sdist and fix return URI, which will have file://...#egg=calcipy\n sdist_uri = session.poetry.build_package(distribution_format=DistributionFormat.SDIST)\n path_sdist = Path(url2pathname(urlparse(sdist_uri).path))\n LOGGER.text_debug('Fixed sdist URI', sdist_uri=sdist_uri, path_sdist=path_sdist)\n # Check with pyroma\n session.install('pyroma>=4.0', '--upgrade')\n # required for \"poetry.core.masonry.api\" build backend\n session.run('python', '-m', 'pip', 'install', 'poetry>=1.3', stdout=True)\n session.run('pyroma', '--file', path_sdist.as_posix(), '--min=9', stdout=True)\n"},{"location":"reference/calcipy/noxfile/#calcipy.noxfile.build_dist","title":"build_dist","text":"build_dist(session)\n Build and test the project files within a controlled environment for repeatability. Source code in calcipy/noxfile/_noxfile.py @nox_session(python=_get_pythons()[-1:], reuse_venv=False)\ndef build_dist(session: Union[NoxSession, NPSession]) -> None: # pragma: no cover\n \"\"\"Build and test the project files within a controlled environment for repeatability.\"\"\"\n dist_path = Path('dist')\n if_found_unlink(dist_path)\n\n # Support 'corallium' by re-implementing \"session.poetry.build_package()\", from:\n # https://github.com/cjolowicz/nox-poetry/blob/5772b66ebff8d5a3351a08ed402d3d31e48be5f8/src/nox_poetry/sessions.py#L233-L255\n # https://github.com/cjolowicz/nox-poetry/blob/5772b66ebff8d5a3351a08ed402d3d31e48be5f8/src/nox_poetry/poetry.py#L111-L154\n output = session.run(*shlex.split('poetry build --format=wheel --no-ansi'),\n external=True, silent=True)\n output = cast(str, output)\n wheel = dist_path / output.split()[-1]\n path_wheel = wheel.resolve().as_uri()\n\n LOGGER.text('Created wheel', path_wheel=path_wheel)\n # Install the wheel and check that imports without any of the optional dependencies\n session.install(path_wheel)\n session.run(*shlex.split('python scripts/check_imports.py'), stdout=True)\n"},{"location":"reference/calcipy/noxfile/#calcipy.noxfile.tests","title":"tests","text":"tests(session)\n Run doit test task for specified python versions. Source code in calcipy/noxfile/_noxfile.py @nox_session(python=_get_pythons(), reuse_venv=True)\ndef tests(session: Union[NoxSession, NPSession]) -> None: # pragma: no cover\n \"\"\"Run doit test task for specified python versions.\"\"\"\n _install_local(session, ['ddict', 'doc', 'lint', 'nox', 'stale', 'tags', 'test'])\n session.run(*shlex.split('pytest ./tests'), stdout=True, env={'RUNTIME_TYPE_CHECKING_MODE': 'WARNING'})\n"},{"location":"reference/calcipy/noxfile/_noxfile/","title":"_noxfile","text":"nox-poetry configuration file. Useful snippets from docs poetry run nox -l\npoetry run nox --list-sessions\n\npoetry run nox -s build_check-3.8 build_dist-3.8 tests-3.8\npoetry run nox --session tests-3.11\n\npoetry run nox --python 3.8\n\npoetry run nox -k \"not build_check and not build_dist\"\n Useful nox snippets # Example conditionally skipping a session\nif not session.interactive:\n session.skip('Cannot run detect-secrets audit in non-interactive shell')\n\n# Install pinned version\nsession.install('detect-secrets==1.0.3')\n\n# Example capturing STDOUT into a file (could do the same for stderr)\npath_stdout = Path('.stdout.txt').resolve()\nwith open(path_stdout, 'w') as out:\n session.run(*shlex.split('echo Hello World!'), stdout=out)\n"},{"location":"reference/calcipy/noxfile/_noxfile/#calcipy.noxfile._noxfile-functions","title":"Functions","text":""},{"location":"reference/calcipy/noxfile/_noxfile/#calcipy.noxfile._noxfile.build_check","title":"build_check","text":"build_check(session)\n Check that the built output meets all checks. Source code in calcipy/noxfile/_noxfile.py @nox_poetry_session(python=_get_pythons()[-1:], reuse_venv=True)\ndef build_check(session: NPSession) -> None: # pragma: no cover\n \"\"\"Check that the built output meets all checks.\"\"\"\n # Build sdist and fix return URI, which will have file://...#egg=calcipy\n sdist_uri = session.poetry.build_package(distribution_format=DistributionFormat.SDIST)\n path_sdist = Path(url2pathname(urlparse(sdist_uri).path))\n LOGGER.text_debug('Fixed sdist URI', sdist_uri=sdist_uri, path_sdist=path_sdist)\n # Check with pyroma\n session.install('pyroma>=4.0', '--upgrade')\n # required for \"poetry.core.masonry.api\" build backend\n session.run('python', '-m', 'pip', 'install', 'poetry>=1.3', stdout=True)\n session.run('pyroma', '--file', path_sdist.as_posix(), '--min=9', stdout=True)\n"},{"location":"reference/calcipy/noxfile/_noxfile/#calcipy.noxfile._noxfile.build_dist","title":"build_dist","text":"build_dist(session)\n Build and test the project files within a controlled environment for repeatability. Source code in calcipy/noxfile/_noxfile.py @nox_session(python=_get_pythons()[-1:], reuse_venv=False)\ndef build_dist(session: Union[NoxSession, NPSession]) -> None: # pragma: no cover\n \"\"\"Build and test the project files within a controlled environment for repeatability.\"\"\"\n dist_path = Path('dist')\n if_found_unlink(dist_path)\n\n # Support 'corallium' by re-implementing \"session.poetry.build_package()\", from:\n # https://github.com/cjolowicz/nox-poetry/blob/5772b66ebff8d5a3351a08ed402d3d31e48be5f8/src/nox_poetry/sessions.py#L233-L255\n # https://github.com/cjolowicz/nox-poetry/blob/5772b66ebff8d5a3351a08ed402d3d31e48be5f8/src/nox_poetry/poetry.py#L111-L154\n output = session.run(*shlex.split('poetry build --format=wheel --no-ansi'),\n external=True, silent=True)\n output = cast(str, output)\n wheel = dist_path / output.split()[-1]\n path_wheel = wheel.resolve().as_uri()\n\n LOGGER.text('Created wheel', path_wheel=path_wheel)\n # Install the wheel and check that imports without any of the optional dependencies\n session.install(path_wheel)\n session.run(*shlex.split('python scripts/check_imports.py'), stdout=True)\n"},{"location":"reference/calcipy/noxfile/_noxfile/#calcipy.noxfile._noxfile.tests","title":"tests","text":"tests(session)\n Run doit test task for specified python versions. Source code in calcipy/noxfile/_noxfile.py @nox_session(python=_get_pythons(), reuse_venv=True)\ndef tests(session: Union[NoxSession, NPSession]) -> None: # pragma: no cover\n \"\"\"Run doit test task for specified python versions.\"\"\"\n _install_local(session, ['ddict', 'doc', 'lint', 'nox', 'stale', 'tags', 'test'])\n session.run(*shlex.split('pytest ./tests'), stdout=True, env={'RUNTIME_TYPE_CHECKING_MODE': 'WARNING'})\n"},{"location":"reference/calcipy/tasks/","title":"tasks","text":""},{"location":"reference/calcipy/tasks/_invoke/","title":"_invoke","text":"Extend Invoke for Calcipy."},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke-classes","title":"Classes","text":""},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke.Collection","title":"Collection","text":" Bases: Collection Source code in calcipy/tasks/_invoke.py class Collection(InvokeCollection):\n\n @classmethod\n def from_module(\n cls,\n module: ModuleType,\n name: Optional[str] = None,\n config: Optional[Dict[str, Any]] = None,\n loaded_from: Optional[str] = None,\n auto_dash_names: Optional[bool] = None,\n ) -> 'InvokeCollection':\n \"\"\"Extend search for a namespace, Task, or deferred task.\"\"\"\n collection = super().from_module(\n module=module,\n name=name,\n config=config,\n loaded_from=loaded_from,\n auto_dash_names=auto_dash_names,\n )\n\n # If tasks were not loaded from a namespace or otherwise found\n if not collection.task_names:\n # Look for any decorated, but deferred \"Tasks\"\n for task in (fxn for fxn in vars(module).values() if hasattr(fxn, TASK_ARGS_ATTR)):\n collection.add_task(task)\n\n return collection\n\n def add_task(\n self,\n task: DeferedTask,\n name: Optional[str] = None,\n aliases: Optional[Tuple[str, ...]] = None,\n default: Optional[bool] = None,\n ) -> None:\n \"\"\"Extend for deferred tasks.\"\"\"\n super().add_task(task=_build_task(task), name=name, aliases=aliases, default=default)\n"},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke.Collection-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke.Collection.add_task","title":"add_task","text":"add_task(task, name=None, aliases=None, default=None)\n Extend for deferred tasks. Source code in calcipy/tasks/_invoke.py def add_task(\n self,\n task: DeferedTask,\n name: Optional[str] = None,\n aliases: Optional[Tuple[str, ...]] = None,\n default: Optional[bool] = None,\n) -> None:\n \"\"\"Extend for deferred tasks.\"\"\"\n super().add_task(task=_build_task(task), name=name, aliases=aliases, default=default)\n"},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke.Collection.from_module","title":"from_module classmethod","text":"from_module(module, name=None, config=None, loaded_from=None, auto_dash_names=None)\n Extend search for a namespace, Task, or deferred task. Source code in calcipy/tasks/_invoke.py @classmethod\ndef from_module(\n cls,\n module: ModuleType,\n name: Optional[str] = None,\n config: Optional[Dict[str, Any]] = None,\n loaded_from: Optional[str] = None,\n auto_dash_names: Optional[bool] = None,\n) -> 'InvokeCollection':\n \"\"\"Extend search for a namespace, Task, or deferred task.\"\"\"\n collection = super().from_module(\n module=module,\n name=name,\n config=config,\n loaded_from=loaded_from,\n auto_dash_names=auto_dash_names,\n )\n\n # If tasks were not loaded from a namespace or otherwise found\n if not collection.task_names:\n # Look for any decorated, but deferred \"Tasks\"\n for task in (fxn for fxn in vars(module).values() if hasattr(fxn, TASK_ARGS_ATTR)):\n collection.add_task(task)\n\n return collection\n"},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke.GlobalTaskOptions","title":"GlobalTaskOptions","text":" Bases: BaseModel Global Task Options. Source code in calcipy/tasks/_invoke.py class GlobalTaskOptions(BaseModel):\n \"\"\"Global Task Options.\"\"\"\n\n working_dir: Path = Field(default_factory=Path.cwd)\n \"\"\"Working directory for the program to use globally.\"\"\"\n\n file_args: List[Path] = Field(default_factory=list)\n \"\"\"List of Paths to modify.\"\"\"\n\n verbose: PositiveInt = Field(default=0, le=3)\n \"\"\"Verbosity level.\"\"\"\n\n keep_going: bool = False\n \"\"\"Continue task execution regardless of failure.\"\"\"\n"},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke.GlobalTaskOptions-attributes","title":"Attributes","text":""},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke.GlobalTaskOptions.file_args","title":"file_args class-attribute instance-attribute","text":"file_args = Field(default_factory=list)\n List of Paths to modify."},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke.GlobalTaskOptions.keep_going","title":"keep_going class-attribute instance-attribute","text":"keep_going = False\n Continue task execution regardless of failure."},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke.GlobalTaskOptions.verbose","title":"verbose class-attribute instance-attribute","text":"verbose = Field(default=0, le=3)\n Verbosity level."},{"location":"reference/calcipy/tasks/_invoke/#calcipy.tasks._invoke.GlobalTaskOptions.working_dir","title":"working_dir class-attribute instance-attribute","text":"working_dir = Field(default_factory=cwd)\n Working directory for the program to use globally."},{"location":"reference/calcipy/tasks/all_tasks/","title":"all_tasks","text":"Tasks can be imported piecemeal or imported in their entirety from here."},{"location":"reference/calcipy/tasks/all_tasks/#calcipy.tasks.all_tasks-attributes","title":"Attributes","text":""},{"location":"reference/calcipy/tasks/all_tasks/#calcipy.tasks.all_tasks.TaskList","title":"TaskList module-attribute","text":"TaskList = List[Union[Call, DeferedTask]]\n List of wrapped or normal task functions."},{"location":"reference/calcipy/tasks/all_tasks/#calcipy.tasks.all_tasks-classes","title":"Classes","text":""},{"location":"reference/calcipy/tasks/all_tasks/#calcipy.tasks.all_tasks-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/all_tasks/#calcipy.tasks.all_tasks.main","title":"main","text":"main(_ctx)\n Run main task pipeline. Source code in calcipy/tasks/all_tasks.py @task(post=with_progress(_MAIN_TASKS))\ndef main(_ctx: Context) -> None:\n \"\"\"Run main task pipeline.\"\"\"\n"},{"location":"reference/calcipy/tasks/all_tasks/#calcipy.tasks.all_tasks.other","title":"other","text":"other(_ctx)\n Run tasks that are otherwise not exercised in main. Source code in calcipy/tasks/all_tasks.py @task(post=with_progress(_OTHER_TASKS))\ndef other(_ctx: Context) -> None:\n \"\"\"Run tasks that are otherwise not exercised in main.\"\"\"\n"},{"location":"reference/calcipy/tasks/all_tasks/#calcipy.tasks.all_tasks.progress","title":"progress","text":"progress(_ctx, *, index, total)\n Progress Task. Source code in calcipy/tasks/all_tasks.py @task(\n help={\n 'index': 'Current index (0-indexed)',\n 'total': 'Total steps',\n },\n show_task_info=False,\n)\ndef progress(_ctx: Context, *, index: int, total: int) -> None:\n \"\"\"Progress Task.\"\"\"\n LOGGER.text('Progress', is_header=True, index=index + 1, total=total)\n"},{"location":"reference/calcipy/tasks/all_tasks/#calcipy.tasks.all_tasks.release","title":"release","text":"release(ctx, *, suffix=None)\n Run release pipeline. Source code in calcipy/tasks/all_tasks.py @task(\n help=cl.bump.help, # pyright: ignore[reportFunctionMemberAccess]\n post=with_progress(\n [\n pack.lock,\n doc.build,\n doc.deploy,\n pack.publish,\n ],\n offset=1,\n ),\n)\ndef release(ctx: Context, *, suffix: cl.SuffixT = None) -> None:\n \"\"\"Run release pipeline.\"\"\"\n cl.bumpz(ctx, suffix=suffix)\n"},{"location":"reference/calcipy/tasks/all_tasks/#calcipy.tasks.all_tasks.summary","title":"summary","text":"summary(_ctx, *, message)\n Summary Task. Source code in calcipy/tasks/all_tasks.py @task(\n help={\n 'message': 'String message to display',\n },\n show_task_info=False,\n)\ndef summary(_ctx: Context, *, message: str) -> None:\n \"\"\"Summary Task.\"\"\"\n LOGGER.text(message, is_header=True)\n"},{"location":"reference/calcipy/tasks/all_tasks/#calcipy.tasks.all_tasks.with_progress","title":"with_progress","text":"with_progress(items, offset=0)\n Inject intermediary \u2018progress\u2019 tasks. items: list of tasks\noffset: Optional offset to shift counters\n Source code in calcipy/tasks/all_tasks.py @beartype\ndef with_progress(items: Any, offset: int = 0) -> TaskList:\n \"\"\"Inject intermediary 'progress' tasks.\n\n Args:\n ----\n items: list of tasks\n offset: Optional offset to shift counters\n\n \"\"\"\n task_items = [_build_task(_t) for _t in items]\n message = 'Running tasks: ' + ', '.join([str(_t.__name__) for _t in task_items])\n tasks: TaskList = [summary.with_kwargs(message=message)] # pyright: ignore[reportFunctionMemberAccess]\n\n total = len(task_items) + offset\n for idx, item in enumerate(task_items):\n tasks += [\n progress.with_kwargs(index=idx + offset, total=total), # pyright: ignore[reportFunctionMemberAccess]\n item,\n ]\n return tasks\n"},{"location":"reference/calcipy/tasks/cl/","title":"cl","text":"Changelog CLI."},{"location":"reference/calcipy/tasks/cl/#calcipy.tasks.cl-attributes","title":"Attributes","text":""},{"location":"reference/calcipy/tasks/cl/#calcipy.tasks.cl.SuffixT","title":"SuffixT module-attribute","text":"SuffixT = Optional[Literal['alpha', 'beta', 'rc']]\n Prerelease Suffix Type."},{"location":"reference/calcipy/tasks/cl/#calcipy.tasks.cl-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/cl/#calcipy.tasks.cl.bump","title":"bump","text":"bump(ctx, *, suffix=None)\n Bumps project version based on commits & settings in pyproject.toml. Source code in calcipy/tasks/cl.py @task(\n pre=[write],\n help={\n 'suffix': 'Specify prerelease suffix for version bump (alpha, beta, rc)',\n },\n)\ndef bump(ctx: Context, *, suffix: SuffixT = None) -> None:\n \"\"\"Bumps project version based on commits & settings in pyproject.toml.\"\"\"\n bumpz(ctx, suffix=suffix)\n"},{"location":"reference/calcipy/tasks/cl/#calcipy.tasks.cl.bumpz","title":"bumpz","text":"bumpz(ctx, *, suffix=None)\n Bumps project version based on commits & settings in pyproject.toml. Source code in calcipy/tasks/cl.py @beartype\ndef bumpz(ctx: Context, *, suffix: SuffixT = None) -> None:\n \"\"\"Bumps project version based on commits & settings in pyproject.toml.\"\"\"\n check_installed(ctx, executable='gh', message=GH_MESSAGE)\n\n opt_cz_args = f' --prerelease={suffix}' if suffix else ''\n run(ctx, f'{python_dir()}/cz bump{opt_cz_args} --annotated-tag --no-verify --gpg-sign')\n\n run(ctx, 'git push origin --tags --no-verify')\n\n get_last_tag = 'git tag --list --sort=-creatordate | head -n 1'\n opt_gh_args = ' --prerelease' if suffix else ''\n run(ctx, f'gh release create --generate-notes $({get_last_tag}){opt_gh_args}')\n"},{"location":"reference/calcipy/tasks/cl/#calcipy.tasks.cl.write","title":"write","text":"write(ctx)\n Write a Changelog file with the raw Git history. Resources: https://keepachangelog.com/en/1.0.0/ https://www.conventionalcommits.org/en/v1.0.0/ https://writingfordevelopers.substack.com/p/how-to-write-a-commit-message https://chris.beams.io/posts/git-commit/ https://semver.org/ https://calver.org/ "},{"location":"reference/calcipy/tasks/cl/#calcipy.tasks.cl.write--returns","title":"Returns","text":"List[DoitAction]: doit actions\n Source code in calcipy/tasks/cl.py @task()\ndef write(ctx: Context) -> None:\n \"\"\"Write a Changelog file with the raw Git history.\n\n Resources:\n\n - https://keepachangelog.com/en/1.0.0/\n - https://www.conventionalcommits.org/en/v1.0.0/\n - https://writingfordevelopers.substack.com/p/how-to-write-a-commit-message\n - https://chris.beams.io/posts/git-commit/\n - https://semver.org/\n - https://calver.org/\n\n Returns\n -------\n List[DoitAction]: doit actions\n\n \"\"\"\n run(ctx, f'{python_dir()}/cz changelog')\n path_cl = get_project_path() / 'CHANGELOG.md'\n if not path_cl.is_file():\n msg = f'Could not locate the changelog at: {path_cl}'\n raise FileNotFoundError(msg)\n path_cl.replace(get_doc_subdir() / path_cl.name)\n"},{"location":"reference/calcipy/tasks/defaults/","title":"defaults","text":"Calcipy-Invoke Defaults."},{"location":"reference/calcipy/tasks/defaults/#calcipy.tasks.defaults-classes","title":"Classes","text":""},{"location":"reference/calcipy/tasks/defaults/#calcipy.tasks.defaults-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/defaults/#calcipy.tasks.defaults.from_ctx","title":"from_ctx","text":"from_ctx(ctx, group, key)\n Safely extract the value from the context or the defaults. Instead of ctx.tests.out_dir use from_ctx(ctx, 'test', 'out_dir') Source code in calcipy/tasks/defaults.py @beartype\ndef from_ctx(ctx: Context, group: str, key: str) -> str:\n \"\"\"Safely extract the value from the context or the defaults.\n\n Instead of `ctx.tests.out_dir` use `from_ctx(ctx, 'test', 'out_dir')`\n\n \"\"\"\n with suppress(KeyError):\n return str(ctx.config[group][key])\n return str(DEFAULTS[group][key])\n"},{"location":"reference/calcipy/tasks/defaults/#calcipy.tasks.defaults.new_collection","title":"new_collection","text":"new_collection()\n Initialize a collection with the combination of merged and project-specific defaults. Source code in calcipy/tasks/defaults.py @beartype\ndef new_collection() -> Collection:\n \"\"\"Initialize a collection with the combination of merged and project-specific defaults.\"\"\"\n ns = Collection('')\n\n # Merge default and user configuration\n ns.configure(DEFAULTS)\n config_path = Path('.calcipy.json')\n if config_path.is_file():\n ns.configure(json.loads(config_path.read_text(encoding='utf-8')))\n\n return ns\n"},{"location":"reference/calcipy/tasks/doc/","title":"doc","text":"Document CLI."},{"location":"reference/calcipy/tasks/doc/#calcipy.tasks.doc-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/doc/#calcipy.tasks.doc.build","title":"build","text":"build(ctx)\n Build documentation with mkdocs. Source code in calcipy/tasks/doc.py @task()\ndef build(ctx: Context) -> None:\n \"\"\"Build documentation with mkdocs.\"\"\"\n auto_doc_path = get_doc_subdir().parent / 'modules'\n write_autoformatted_md_sections()\n delete_dir(auto_doc_path)\n _diagram_task(ctx, auto_doc_path)\n\n # Find and trim trailing whitespace\n for path_md in auto_doc_path.rglob('*.md'):\n trim_trailing_whitespace(path_md)\n\n run(ctx, f'{python_dir()}/mkdocs build --site-dir {get_out_dir()}')\n"},{"location":"reference/calcipy/tasks/doc/#calcipy.tasks.doc.deploy","title":"deploy","text":"deploy(ctx)\n Deploy docs to the Github gh-pages branch. Source code in calcipy/tasks/doc.py @task()\ndef deploy(ctx: Context) -> None:\n \"\"\"Deploy docs to the Github `gh-pages` branch.\"\"\"\n if _is_mkdocs_local(): # pragma: no cover\n raise NotImplementedError('Not yet configured to deploy documentation without \"use_directory_urls\"')\n\n with suppress(UnexpectedExit):\n run(ctx, 'pre-commit uninstall') # To prevent pre-commit failures when mkdocs calls push\n run(ctx, f'{python_dir()}/mkdocs gh-deploy --force')\n with suppress(UnexpectedExit):\n run(ctx, 'pre-commit install') # Restore pre-commit\n"},{"location":"reference/calcipy/tasks/doc/#calcipy.tasks.doc.get_out_dir","title":"get_out_dir","text":"get_out_dir()\n Retrieve the mkdocs-specified site directory. Source code in calcipy/tasks/doc.py @beartype\ndef get_out_dir() -> Path:\n \"\"\"Retrieve the mkdocs-specified site directory.\"\"\"\n mkdocs_config = read_yaml_file(get_project_path() / MKDOCS_CONFIG)\n return Path(mkdocs_config.get('site_dir', 'releases/site'))\n"},{"location":"reference/calcipy/tasks/doc/#calcipy.tasks.doc.watch","title":"watch","text":"watch(ctx)\n Serve local documentation for local editing. Source code in calcipy/tasks/doc.py @task()\ndef watch(ctx: Context) -> None:\n \"\"\"Serve local documentation for local editing.\"\"\"\n if _is_mkdocs_local(): # pragma: no cover\n path_doc_index = get_out_dir() / 'index.html'\n open_in_browser(path_doc_index)\n else: # pragma: no cover\n webbrowser.open('http://localhost:8000')\n run(ctx, f'{python_dir()}/mkdocs serve --dirtyreload')\n"},{"location":"reference/calcipy/tasks/executable_utils/","title":"executable_utils","text":"Utilities for working in calcipy\u2019s python environment."},{"location":"reference/calcipy/tasks/executable_utils/#calcipy.tasks.executable_utils-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/executable_utils/#calcipy.tasks.executable_utils.check_installed","title":"check_installed","text":"check_installed(ctx, executable, message)\n If the required executable isn\u2019t present, raise a clear user error. Source code in calcipy/tasks/executable_utils.py @beartype\ndef check_installed(ctx: Context, executable: str, message: str) -> None:\n \"\"\"If the required executable isn't present, raise a clear user error.\"\"\"\n res = run(ctx, f'which {executable}', warn=True, hide=True)\n if not res or res.exited == 1:\n raise RuntimeError(message)\n"},{"location":"reference/calcipy/tasks/executable_utils/#calcipy.tasks.executable_utils.python_dir","title":"python_dir cached","text":"python_dir()\n Run an executable from the currently active Python directory. Source code in calcipy/tasks/executable_utils.py @lru_cache(maxsize=1)\ndef python_dir() -> str:\n \"\"\"Run an executable from the currently active Python directory.\"\"\"\n return str(resolve_python().parent)\n"},{"location":"reference/calcipy/tasks/executable_utils/#calcipy.tasks.executable_utils.python_m","title":"python_m cached","text":"python_m()\n Return the active python path and -m flag. Source code in calcipy/tasks/executable_utils.py @lru_cache(maxsize=1)\ndef python_m() -> str:\n \"\"\"Return the active python path and `-m` flag.\"\"\"\n return f'{resolve_python()} -m'\n"},{"location":"reference/calcipy/tasks/executable_utils/#calcipy.tasks.executable_utils.resolve_python","title":"resolve_python cached","text":"resolve_python()\n Resolve the user\u2019s Python path based on sys. Source code in calcipy/tasks/executable_utils.py @lru_cache(maxsize=1)\ndef resolve_python() -> Path:\n \"\"\"Resolve the user's Python path based on `sys`.\"\"\"\n python_path = Path(sys.executable)\n with suppress(ValueError):\n return python_path.relative_to(Path.cwd())\n return python_path\n"},{"location":"reference/calcipy/tasks/lint/","title":"lint","text":"Lint CLI."},{"location":"reference/calcipy/tasks/lint/#calcipy.tasks.lint-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/lint/#calcipy.tasks.lint.check","title":"check","text":"check(ctx)\n Run ruff as check-only. Source code in calcipy/tasks/lint.py @task(default=True)\ndef check(ctx: Context) -> None:\n \"\"\"Run ruff as check-only.\"\"\"\n _inner_task(ctx, command='ruff check')\n"},{"location":"reference/calcipy/tasks/lint/#calcipy.tasks.lint.fix","title":"fix","text":"fix(ctx, *, unsafe=False)\n Run ruff and apply fixes. Source code in calcipy/tasks/lint.py @task(\n help={\n 'unsafe': 'if provided, attempt even fixes considered unsafe',\n },\n)\ndef fix(ctx: Context, *, unsafe: bool = False) -> None:\n \"\"\"Run ruff and apply fixes.\"\"\"\n cli_args = '--fix'\n if unsafe:\n cli_args += ' --unsafe-fixes'\n _inner_task(ctx, command='ruff check', cli_args=cli_args)\n"},{"location":"reference/calcipy/tasks/lint/#calcipy.tasks.lint.pre_commit","title":"pre_commit","text":"pre_commit(ctx, *, no_update=False)\n Run pre-commit. Source code in calcipy/tasks/lint.py @task(\n help={\n 'no_update': 'Skip updating the pre-commit hooks',\n },\n)\ndef pre_commit(ctx: Context, *, no_update: bool = False) -> None:\n \"\"\"Run pre-commit.\"\"\"\n check_installed(ctx, executable='pre-commit', message=PRE_COMMIT_MESSAGE)\n\n run(ctx, 'pre-commit install')\n if not no_update:\n run(ctx, 'pre-commit autoupdate')\n\n stages_cli = ' '.join(f'--hook-stage {stg}' for stg in ALL_PRE_COMMIT_HOOK_STAGES)\n run(ctx, f'pre-commit run --all-files {stages_cli}')\n"},{"location":"reference/calcipy/tasks/lint/#calcipy.tasks.lint.pylint","title":"pylint","text":"pylint(ctx, *, report=False)\n Run pylint. Source code in calcipy/tasks/lint.py @task(\n help={\n 'report': 'if provided, show the pylint summary report',\n },\n)\ndef pylint(ctx: Context, *, report: bool = False) -> None:\n \"\"\"Run pylint.\"\"\"\n cli_args = '--report=y' if report else ''\n _inner_task(ctx, command='pylint', cli_args=cli_args)\n"},{"location":"reference/calcipy/tasks/lint/#calcipy.tasks.lint.watch","title":"watch","text":"watch(ctx)\n Run ruff as check-only. Source code in calcipy/tasks/lint.py @task()\ndef watch(ctx: Context) -> None:\n \"\"\"Run ruff as check-only.\"\"\"\n _inner_task(ctx, command='ruff check', cli_args='--watch --show-source')\n"},{"location":"reference/calcipy/tasks/nox/","title":"nox","text":"Nox CLI."},{"location":"reference/calcipy/tasks/nox/#calcipy.tasks.nox-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/nox/#calcipy.tasks.nox.noxfile","title":"noxfile","text":"noxfile(ctx, *, session='')\n Run nox from the local noxfile. Source code in calcipy/tasks/nox.py @task(\n default=True,\n help={\n 'session': 'Optional session to run',\n },\n)\ndef noxfile(ctx: Context, *, session: str = '') -> None:\n \"\"\"Run nox from the local noxfile.\"\"\"\n cli_args = ['--session', session] if session else []\n run(ctx, f'{python_dir()}/nox --error-on-missing-interpreters {\" \".join(cli_args)}')\n"},{"location":"reference/calcipy/tasks/pack/","title":"pack","text":"Packaging CLI."},{"location":"reference/calcipy/tasks/pack/#calcipy.tasks.pack-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/pack/#calcipy.tasks.pack.bump_tag","title":"bump_tag","text":"bump_tag(ctx, *, tag, tag_prefix='', pkg_name='')\n Experiment with bumping the git tag using griffe. Example for calcipy: ./run pack.bump-tag --tag=\"$(git tag -l \"*\" | sort | head -n 5 | tail -n 1)\" --tag-prefix=\"\"\n Source code in calcipy/tasks/pack.py @task(\n help={\n 'tag': 'Last tag, can be provided with `--tag=\"$(git tag -l \"v*\" | sort | tail -n 1)\"`',\n 'tag_prefix': 'Optional tag prefix, such as \"v\"',\n 'pkg_name': 'Optional package name. If not provided, will read the poetry pyproject.toml file',\n },\n)\ndef bump_tag(ctx: Context, *, tag: str, tag_prefix: str = '', pkg_name: str = '') -> None: # noqa: ARG001\n \"\"\"Experiment with bumping the git tag using `griffe`.\n\n Example for `calcipy`:\n\n ```sh\n ./run pack.bump-tag --tag=\"$(git tag -l \"*\" | sort | head -n 5 | tail -n 1)\" --tag-prefix=\"\"\n ```\n\n \"\"\"\n if not tag:\n raise ValueError('tag must not be empty')\n if not pkg_name:\n poetry_config = file_helpers.read_pyproject()['tool']['poetry']\n pkg_name = poetry_config['name']\n\n from calcipy.experiments import bump_programmatically # noqa: PLC0415\n\n new_version = bump_programmatically.bump_tag(\n pkg_name=pkg_name,\n tag=tag,\n tag_prefix=tag_prefix,\n )\n LOGGER.text(new_version)\n"},{"location":"reference/calcipy/tasks/pack/#calcipy.tasks.pack.check_licenses","title":"check_licenses","text":"check_licenses(ctx)\n Check licenses for compatibility with licensecheck. Source code in calcipy/tasks/pack.py @task()\ndef check_licenses(ctx: Context) -> None:\n \"\"\"Check licenses for compatibility with `licensecheck`.\"\"\"\n res = run(ctx, 'which licensecheck', warn=True, hide=True)\n if not res or res.exited == 1:\n LOGGER.warning('`licensecheck` not found. installing with pipx')\n run(ctx, 'pipx install licensecheck')\n run(ctx, 'licensecheck')\n"},{"location":"reference/calcipy/tasks/pack/#calcipy.tasks.pack.install_extras","title":"install_extras","text":"install_extras(ctx)\n Run poetry install with all extras. Source code in calcipy/tasks/pack.py @task(pre=[lock])\ndef install_extras(ctx: Context) -> None:\n \"\"\"Run poetry install with all extras.\"\"\"\n poetry_config = file_helpers.read_pyproject()['tool']['poetry']\n extras = (poetry_config.get('extras') or {}).keys()\n run(ctx, ' '.join(['poetry install --sync', *[f'--extras={ex}' for ex in extras]]))\n"},{"location":"reference/calcipy/tasks/pack/#calcipy.tasks.pack.lock","title":"lock","text":"lock(ctx)\n Ensure poetry.lock is up-to-date. Source code in calcipy/tasks/pack.py @task()\ndef lock(ctx: Context) -> None:\n \"\"\"Ensure poetry.lock is up-to-date.\"\"\"\n if can_skip.can_skip(prerequisites=[PROJECT_TOML], targets=[LOCK]):\n return # Exit early\n\n run(ctx, 'poetry lock --no-update')\n"},{"location":"reference/calcipy/tasks/pack/#calcipy.tasks.pack.publish","title":"publish","text":"publish(ctx, *, to_test_pypi=False)\n Build the distributed format(s) and publish. Source code in calcipy/tasks/pack.py @task(\n help={\n 'to_test_pypi': 'Publish to the TestPyPi repository',\n },\n)\ndef publish(ctx: Context, *, to_test_pypi: bool = False) -> None:\n \"\"\"Build the distributed format(s) and publish.\"\"\"\n run(ctx, f'{python_dir()}/nox --error-on-missing-interpreters --session build_dist build_check')\n\n cmd = 'poetry publish'\n if to_test_pypi:\n cmd += ' --repository testpypi'\n run(ctx, cmd)\n"},{"location":"reference/calcipy/tasks/stale/","title":"stale","text":"Stale Packages CLI."},{"location":"reference/calcipy/tasks/stale/#calcipy.tasks.stale-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/stale/#calcipy.tasks.stale.check_for_stale_packages","title":"check_for_stale_packages","text":"check_for_stale_packages(ctx, *, stale_months=48)\n Identify stale dependencies. Source code in calcipy/tasks/stale.py @task(\n default=True,\n help={\n 'stale_months': 'Cutoff in months for when a package may be stale enough to be a risk',\n },\n)\ndef check_for_stale_packages(ctx: Context, *, stale_months: int = 48) -> None: # noqa: ARG001\n \"\"\"Identify stale dependencies.\"\"\"\n cfsp(stale_months=stale_months)\n"},{"location":"reference/calcipy/tasks/tags/","title":"tags","text":"Code Tag Collector CLI."},{"location":"reference/calcipy/tasks/tags/#calcipy.tasks.tags-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/tags/#calcipy.tasks.tags.collect_code_tags","title":"collect_code_tags","text":"collect_code_tags(ctx, base_dir='.', doc_sub_dir='', filename=None, tag_order='', regex='', ignore_patterns='')\n Create a CODE_TAG_SUMMARY.md with a table for TODO- and FIXME-style code comments. Source code in calcipy/tasks/tags.py @task(\n default=True,\n help={\n 'base_dir': 'Working Directory',\n 'doc_sub_dir': 'Subdirectory for output of the code tag summary file',\n 'filename': 'Code Tag Summary Filename',\n 'tag_order': 'Ordered list of code tags to locate (Comma-separated)',\n 'regex': 'Custom Code Tag Regex. Must contain \"{tag}\"',\n 'ignore_patterns': 'Glob patterns to ignore files and directories when searching (Comma-separated)',\n },\n)\ndef collect_code_tags( # noqa: PLR0917\n ctx: Context,\n base_dir: str = '.',\n doc_sub_dir: str = '',\n filename: Optional[str] = None,\n tag_order: str = '',\n regex: str = '',\n ignore_patterns: str = '',\n) -> None:\n \"\"\"Create a `CODE_TAG_SUMMARY.md` with a table for TODO- and FIXME-style code comments.\"\"\"\n pth_base_dir = Path(base_dir).resolve()\n pth_docs = pth_base_dir / doc_sub_dir if doc_sub_dir else get_doc_subdir()\n if filename and '/' in filename:\n raise RuntimeError('Unexpected slash in filename. You should consider setting `--doc-sub-dir` instead')\n path_tag_summary = pth_docs / (filename or from_ctx(ctx, 'tags', 'filename'))\n patterns = (ignore_patterns or from_ctx(ctx, 'tags', 'ignore_patterns')).split(',')\n paths_source = find_project_files(pth_base_dir, ignore_patterns=[pattern for pattern in patterns if pattern])\n\n write_code_tag_file(\n path_tag_summary=path_tag_summary,\n paths_source=paths_source,\n base_dir=pth_base_dir,\n regex=regex,\n tags=tag_order,\n header='# Collected Code Tags',\n )\n"},{"location":"reference/calcipy/tasks/test/","title":"test","text":"Test CLI."},{"location":"reference/calcipy/tasks/test/#calcipy.tasks.test-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/test/#calcipy.tasks.test.check","title":"check","text":"check(_ctx)\n Run pytest checks, such as identifying . Source code in calcipy/tasks/test.py @task()\ndef check(_ctx: Context) -> None:\n \"\"\"Run pytest checks, such as identifying .\"\"\"\n if duplciates := check_duplicate_test_names.run(Path('tests')):\n raise RuntimeError(f'Duplicate test names found ({duplciates}). See above for details.') # noqa: EM102\n"},{"location":"reference/calcipy/tasks/test/#calcipy.tasks.test.coverage","title":"coverage","text":"coverage(ctx, *, min_cover=0, out_dir=None, view=False)\n Generate useful coverage outputs after running pytest. Creates coverage.json used in doc.build Source code in calcipy/tasks/test.py @task(\n help={\n 'min_cover': 'Fail if coverage less than threshold',\n 'out_dir': 'Optional path to coverage directory. Typically \".cover\" or \"releases/tests\"',\n 'view': 'If True, open the created files',\n },\n)\ndef coverage(ctx: Context, *, min_cover: int = 0, out_dir: Optional[str] = None, view: bool = False) -> None:\n \"\"\"Generate useful coverage outputs after running pytest.\n\n Creates `coverage.json` used in `doc.build`\n\n \"\"\"\n pkg_name = read_package_name()\n _inner_task(ctx, cli_args='', min_cover=min_cover,\n command=f'coverage run --branch --source={pkg_name} --module pytest')\n\n cov_dir = Path(out_dir or from_ctx(ctx, 'test', 'out_dir'))\n cov_dir.mkdir(exist_ok=True, parents=True)\n print() # noqa: T201\n for cli_args in (\n 'report --show-missing', # Write to STDOUT\n f'html --directory={cov_dir}', # Write to HTML\n 'json', # Create coverage.json file for \"_handle_coverage\"\n ):\n run(ctx, f'{python_dir()}/coverage {cli_args}')\n\n if view: # pragma: no cover\n open_in_browser(cov_dir / 'index.html')\n"},{"location":"reference/calcipy/tasks/test/#calcipy.tasks.test.pytest","title":"pytest","text":"pytest(ctx, *, keyword='', marker='', min_cover=0)\n Run pytest with default arguments. Additional arguments can be set in the environment variable \u2018PYTEST_ADDOPTS\u2019 Source code in calcipy/tasks/test.py @task(\n default=True,\n help={\n 'min_cover': 'Fail if coverage less than threshold',\n **KM_HELP,\n },\n)\ndef pytest(ctx: Context, *, keyword: str = '', marker: str = '', min_cover: int = 0) -> None:\n \"\"\"Run pytest with default arguments.\n\n Additional arguments can be set in the environment variable 'PYTEST_ADDOPTS'\n\n \"\"\"\n pkg_name = read_package_name()\n durations = '--durations=25 --durations-min=\"0.1\"'\n _inner_task(ctx,\n cli_args=f' --cov={pkg_name} --cov-branch --cov-report=term-missing {durations}',\n keyword=keyword, marker=marker, min_cover=min_cover)\n"},{"location":"reference/calcipy/tasks/test/#calcipy.tasks.test.step","title":"step","text":"step(ctx, *, keyword='', marker='')\n Run pytest optimized to stop on first error. Source code in calcipy/tasks/test.py @task(help=KM_HELP)\ndef step(ctx: Context, *, keyword: str = '', marker: str = '') -> None:\n \"\"\"Run pytest optimized to stop on first error.\"\"\"\n _inner_task(ctx, cli_args=_STEPWISE_ARGS, keyword=keyword, marker=marker)\n"},{"location":"reference/calcipy/tasks/test/#calcipy.tasks.test.watch","title":"watch","text":"watch(ctx, *, keyword='', marker='')\n Run pytest with polling and optimized to stop on first error. Source code in calcipy/tasks/test.py @task(help=KM_HELP)\ndef watch(ctx: Context, *, keyword: str = '', marker: str = '') -> None:\n \"\"\"Run pytest with polling and optimized to stop on first error.\"\"\"\n _inner_task(ctx, cli_args=_STEPWISE_ARGS, keyword=keyword, marker=marker, command='ptw . --now')\n"},{"location":"reference/calcipy/tasks/types/","title":"types","text":"Types CLI."},{"location":"reference/calcipy/tasks/types/#calcipy.tasks.types-functions","title":"Functions","text":""},{"location":"reference/calcipy/tasks/types/#calcipy.tasks.types.basedpyright","title":"basedpyright","text":"basedpyright(ctx)\n Run basedpyright. Source code in calcipy/tasks/types.py @task()\ndef basedpyright(ctx: Context) -> None:\n \"\"\"Run basedpyright.\"\"\"\n _inner_task(ctx, command=f'{python_dir()}/basedpyright')\n"},{"location":"reference/calcipy/tasks/types/#calcipy.tasks.types.mypy","title":"mypy","text":"mypy(ctx)\n Run mypy. Source code in calcipy/tasks/types.py @task()\ndef mypy(ctx: Context) -> None:\n \"\"\"Run mypy.\"\"\"\n _inner_task(ctx, command=f'{python_dir()}/mypy')\n"},{"location":"reference/calcipy/tasks/types/#calcipy.tasks.types.pyright","title":"pyright","text":"pyright(ctx)\n Run pyright. Source code in calcipy/tasks/types.py @task()\ndef pyright(ctx: Context) -> None:\n \"\"\"Run pyright.\"\"\"\n check_installed(ctx, executable='pyright', message=PYRIGHT_MESSAGE)\n _inner_task(ctx, command='pyright')\n"}]} \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz index 0766f0a9..9c8c0c70 100644 Binary files a/sitemap.xml.gz and b/sitemap.xml.gz differ
calcipy is a Python package that implements best practices such as code style (linting, auto-fixes), documentation, CI/CD, and logging. Like the calcium carbonate in hard coral, packages can be built on the calcipy foundation.
calcipy
calcipy has some configurability, but is tailored for my particular use cases. If you want the same sort of functionality, there are a number of alternatives to consider:
Calcipy needs a few static files managed using copier and a template project: kyleking/calcipy_template
You can quickly use the template to create a new project or add calcipy to an existing one:
# Install copier. pipx is recommended\npipx install copier\n\n# To create a new project\ncopier copy gh:KyleKing/calcipy_template new_project\ncd new_project\n\n# Or convert/update an existing one\ncd my_project\ncopier copy gh:KyleKing/calcipy_template .\ncopier update\n
Additionally, calcipy can be run as a CLI application without adding the package as a dependency.
Quick Start:
# For the CLI, only install a few of the extras which can be used from a few different CLI commands\npipx install 'calcipy[lint,tags]'\n\n# Use 'tags' to create a CODE_TAG_SUMMARY of the specified directory\ncalcipy-tags tags --help\ncalcipy-tags tags --base-dir=~/path/to/my_project\n\n# You can list all provided CLI commands with\npipx list\n
venvs are in ~/.local/pipx/venvs\napps are exposed on your $PATH at ~/.local/bin\n package calcipy 1.4.0, installed using Python 3.11.4\n - calcipy\n - calcipy-lint\n - calcipy-pack\n - calcipy-tags\n - calcipy-types\n
Note: the CLI output below is compressed for readability, but you can try running each of these commands locally to see the most up-to-date documentation and the full set of options. The \u201cUsage\u201d, \u201cCore options\u201d, and \u201cGlobal Task Options\u201d are the same for each subsequent command, so they are excluded for brevity.
> calcipy-lint\nUsage: calcipy-lint [--core-opts] <subcommand> [--subcommand-opts] ...\n\nCore options:\n\n --complete Print tab-completion candidates for given parse remainder.\n --hide=STRING Set default value of run()'s 'hide' kwarg.\n --print-completion-script=STRING Print the tab-completion script for your preferred shell (bash|zsh|fish).\n --prompt-for-sudo-password Prompt user at start of session for the sudo.password config value.\n --write-pyc Enable creation of .pyc files.\n -d, --debug Enable debug output.\n -D INT, --list-depth=INT When listing tasks, only show the first INT levels.\n -e, --echo Echo executed commands before running.\n -f STRING, --config=STRING Runtime configuration file to use.\n -F STRING, --list-format=STRING Change the display format used when listing tasks. Should be one of: flat (default), nested, json.\n -h [STRING], --help[=STRING] Show core or per-task help and exit.\n -l [STRING], --list[=STRING] List available tasks, optionally limited to a namespace.\n -p, --pty Use a pty when executing shell commands.\n -R, --dry Echo commands instead of running.\n -T INT, --command-timeout=INT Specify a global command execution timeout, in seconds.\n -V, --version Show version and exit.\n -w, --warn-only Warn, instead of failing, when shell commands fail.\n\nSubcommands:\n\n lint.autopep8 Run autopep8.\n lint.check (lint) Run ruff as check-only.\n lint.fix Run ruff and apply fixes.\n lint.pre-commit Run pre-commit.\n lint.pylint Run pylint.\n lint.security Attempt to identify possible security vulnerabilities.\n lint.watch Run ruff as check-only.\n\nGlobal Task Options:\n\n *file_args List of Paths available globally to all tasks. Will resolve paths with working_dir\n --keep-going Continue running tasks even on failure\n --working_dir=STRING Set the cwd for the program. Example: \"../run --working-dir .. lint test\"\n -v,-vv,-vvv Globally configure logger verbosity (-vvv for most verbose)\n\n> calcipy-pack\n\nSubcommands:\n\n pack.check-licenses Check licenses for compatibility with `licensecheck`.\n pack.install-extras Run poetry install with all extras.\n pack.lock Ensure poetry.lock is up-to-date.\n pack.publish Build the distributed format(s) and publish.\n\n> calcipy-tags\n\nSubcommands:\n\n tags.collect-code-tags (tags) Create a `CODE_TAG_SUMMARY.md` with a table for TODO- and FIXME-style code comments.\n\n> calcipy-types\n\nSubcommands:\n\n types.basedpyright Run basedpyright.\n types.mypy Run mypy.\n types.pyright Run pyright.\n
calcipy can also be used as a pre-commit task by adding the below snippet to your pre-commit file:
pre-commit
repos:\n - repo: https://github.com/KyleKing/calcipy\n rev: main\n hooks:\n - id: tags\n - id: lint-fix\n - id: types\n
See the Open Issues and/or the CODE_TAG_SUMMARY. For release history, see the CHANGELOG.
Open Issues
We welcome pull requests! For your pull request to be accepted smoothly, we suggest that you first open a GitHub issue to discuss your idea. For resources on getting started with the code base, see the below documentation:
We follow the Contributor Covenant Code of Conduct.
We try to reasonably meet most aspects of the \u201cOpenSSF scorecard\u201d from Open Source Insights
If you have any security issue to report, please contact the project maintainers privately. You can reach us at dev.act.kyle@gmail.com.
LICENSE
nest invoke-specific code in tasks/
drop pytest-html and conftest completely
use sqlite3 for pre-commit concurrent doit access
bump Python to 3.8.4 and drop 3.7
Found code tags for TODO (6), PLANNED (1)
git clone https://github.com/kyleking/calcipy.git\ncd calcipy\npoetry install --sync\npoetry run calcipy-pack pack.install-extras\n\n# See the available tasks\npoetry run calcipy\n# Or use a local 'run' file (so that 'calcipy' can be extended)\n./run\n\n# Run the default task list (lint, auto-format, test coverage, etc.)\n./run main\n\n# Make code changes and run specific tasks as needed:\n./run lint.fix test\n
For testing, create an account on TestPyPi. Replace ... with the API token generated on TestPyPi or PyPi respectively
...
poetry config repositories.testpypi https://test.pypi.org/legacy/\npoetry config pypi-token.testpypi ...\n\n./run main pack.publish --to-test-pypi\n# If you didn't configure a token, you will need to provide your username and password to publish\n
To publish to the real PyPi
poetry config pypi-token.pypi ...\n./run release\n\n# Or for a pre-release\n./run release --suffix=rc\n
calcipy/__init__.py
calcipy/can_skip.py
calcipy/check_for_stale_packages/__init__.py
calcipy/check_for_stale_packages/_check_for_stale_packages.py
calcipy/cli.py
calcipy/code_tag_collector/__init__.py
calcipy/code_tag_collector/_collector.py
calcipy/dot_dict/__init__.py
calcipy/dot_dict/_dot_dict.py
calcipy/experiments/__init__.py
calcipy/experiments/bump_programmatically.py
calcipy/experiments/check_duplicate_test_names.py
calcipy/file_search.py
calcipy/invoke_helpers.py
calcipy/md_writer/__init__.py
calcipy/md_writer/_writer.py
calcipy/noxfile/__init__.py
calcipy/noxfile/_noxfile.py
calcipy/scripts.py
calcipy/tasks/__init__.py
calcipy/tasks/_invoke.py
calcipy/tasks/all_tasks.py
calcipy/tasks/cl.py
calcipy/tasks/defaults.py
calcipy/tasks/doc.py
calcipy/tasks/executable_utils.py
calcipy/tasks/lint.py
calcipy/tasks/nox.py
calcipy/tasks/pack.py
calcipy/tasks/stale.py
calcipy/tasks/tags.py
calcipy/tasks/test.py
calcipy/tasks/types.py
Generated on: 2024-06-07
calcipy 1.0.0
calcipy v1 was a complete rewrite to switch from doit to invoke:
v1
doit
invoke
dodo.py
doit excelled at clearly delineated task output and run summary, but invoke isn\u2019t designed that way. I would like to improve the CLI output, but the benefits are worth this tradeoff.
calcipy v0 was built on doit and thus required a dodo.py file. I began adding cement to support a separate CLI for calcipy installed with pipx, but that required a lot of boilerplate code. With doit, the string command needed to be complete at task evaluation rather than runtime, so globbing files couldn\u2019t be resolved lazily.
v0
cement
pipx
While refactoring, the global configuration was mostly removed (DoitGlobals) along with a few tasks, but the main functionality is still present. Any project dependent on calcipy will need substantial changes. The easiest way to start migrating is to run copier copy gh:KyleKing/calcipy_template . for calcipy_template
DoitGlobals
copier copy gh:KyleKing/calcipy_template .
It turns out that switching to invoke appears to have only saved 100ms
> hyperfine -m 20 --warmup 5 \"poetry run python -c 'print(1)'\"\nBenchmark 1: poetry run python -c 'print(1)'\nTime (mean \u00b1 \u03c3): 377.9 ms \u00b1 3.1 ms [User: 235.0 ms, System: 61.8 ms]\nRange (min \u2026 max): 372.7 ms \u2026 384.0 ms 20 runs\n> hyperfine -m 20 --warmup 5 ./run\nBenchmark 1: ./run\nTime (mean \u00b1 \u03c3): 936.0 ms \u00b1 26.9 ms [User: 1548.2 ms, System: 1687.7 ms]\nRange (min \u2026 max): 896.4 ms \u2026 1009.4 ms 20 runs\n> hyperfine -m 20 --warmup 5 \"poetry run calcipy_tags\"\nBenchmark 1: poetry run calcipy_tags\nTime (mean \u00b1 \u03c3): 618.5 ms \u00b1 29.7 ms [User: 1536.8 ms, System: 1066.2 ms]\nRange (min \u2026 max): 578.2 ms \u2026 694.9 ms 20 runs\n> hyperfine -m 20 --warmup 5 \"poetry run doit list\"\nBenchmark 1: poetry run doit list\nTime (mean \u00b1 \u03c3): 1.002 s \u00b1 0.015 s [User: 1.643 s, System: 1.682 s]\nRange (min \u2026 max): 0.974 s \u2026 1.023 s 20 runs\n
Additionally, the major decrease in dependencies will make install and update actions much faster. With the recommended extras installed, calcipy-v1 has 124 dependencies (with all extras, 164) vs. calcipy-v0\u2019s 259. Counted with: cat .calcipy_packaging.lock | jq 'keys' | wc -l
calcipy-v1
calcipy-v0
cat .calcipy_packaging.lock | jq 'keys' | wc -l
Accounting for code extracted to corallium, the overall number of lines decreased from 1772 to 1550 or only 12%, while increasing the CLI and pre-commit capabilities.
corallium
~/calcipy-v0 > cloc calcipy\n-------------------------------------------------------------------------------\nLanguage files blank comment code\n-------------------------------------------------------------------------------\nPython 26 942 1075 1772\n-------------------------------------------------------------------------------\nSUM: 26 942 1075 1772\n-------------------------------------------------------------------------------\n~/calcipy > cloc calcipy\n-------------------------------------------------------------------------------\nLanguage files blank comment code\n-------------------------------------------------------------------------------\nPython 27 454 438 1185\n-------------------------------------------------------------------------------\nSUM: 27 454 438 1185\n-------------------------------------------------------------------------------\n~/corallium > cloc corallium\n-------------------------------------------------------------------------------\nLanguage files blank comment code\n-------------------------------------------------------------------------------\nPython 7 176 149 365\n-------------------------------------------------------------------------------\nSUM: 7 176 149 365\n-------------------------------------------------------------------------------\n\n~/calcipy > cloc tests\n-------------------------------------------------------------------------------\nLanguage files blank comment code\n-------------------------------------------------------------------------------\nYAML 2 0 0 580\nPython 19 176 68 578\nJSON 2 0 0 60\nMarkdown 3 9 10 8\nText 1 0 0 2\n-------------------------------------------------------------------------------\nSUM: 27 185 78 1228\n-------------------------------------------------------------------------------\n~/calcipy-v0 > cloc tests\n-------------------------------------------------------------------------------\nLanguage files blank comment code\n-------------------------------------------------------------------------------\nJSON 30 0 0 762\nYAML 2 0 0 580\nPython 24 314 186 578\nMarkdown 3 9 10 8\n-------------------------------------------------------------------------------\nSUM: 59 323 196 1928\n-------------------------------------------------------------------------------\n~/corallium > cloc tests\n-------------------------------------------------------------------------------\nLanguage files blank comment code\n-------------------------------------------------------------------------------\nPython 6 36 15 69\nMarkdown 1 1 0 2\n-------------------------------------------------------------------------------\nSUM: 7 37 15 71\n-------------------------------------------------------------------------------\n
I would like to restore the doit task summary, but invoke\u2019s architecture doesn\u2019t really make this possible. The --continue option was extremely useful, but that also might not be achievable.
--continue
> poetry run doit run\n. format_recipes > [\n Python: function format_recipes\n]\n\n2023-02-19 10:40:23.954 | INFO | recipes.formatter:_write_toc:287 - Creating TOC for: ./recipes/docs/breakfast\n2023-02-19 10:40:23.957 | INFO | recipes.formatter:_write_toc:287 - Creating TOC for: ./recipes/docs/rice\n2023-02-19 10:40:23.959 | INFO | recipes.formatter:_write_toc:287 - Creating TOC for: ./recipes/docs/meals\n2023-02-19 10:40:23.964 | INFO | recipes.formatter:_write_toc:287 - Creating TOC for: ./recipes/docs/seafood\n2023-02-19 10:40:23.967 | INFO | recipes.formatter:_write_toc:287 - Creating TOC for: ./recipes/docs/pizza\n2023-02-19 10:40:23.969 | INFO | recipes.formatter:_write_toc:287 - Creating TOC for: ./recipes/docs/poultry\n2023-02-19 10:40:23.972 | INFO | recipes.formatter:_write_toc:287 - Creating TOC for: ./recipes/docs/sushi\n. collect_code_tags > [\n Python: function write_code_tag_file\n]\n\n. cl_write > [\n Cmd: poetry run cz changelog\n Python: function _move_cl\n]\n\n. lock > [\n Cmd: poetry lock --no-update\n]\n\nResolving dependencies...\n. nox_coverage > [\n Cmd: poetry run nox --error-on-missing-interpreters --session coverage\n]\n\n...\n\ndoit> Summary:\ndoit> format_recipes was successful\ndoit> collect_code_tags was successful\ndoit> cl_write was successful\ndoit> lock was successful\ndoit> nox_coverage was successful\ndoit> auto_format was successful\ndoit> document was successful\ndoit> check_for_stale_packages was successful\ndoit> pre_commit_hooks failed (red)\ndoit> lint_project was not run\ndoit> static_checks was not run\ndoit> security_checks was not run\ndoit> check_types was not run\n
We use Commitizen to manage both an auto-generated Changelog and incrementing the release version following semver. For both of these automated outputs to work well, please follow the Conventional Commits style, which is described in more detail below.
type(scope): description
!
refactor!: drop support for Node 6
fix : PATCH // feat : MINOR // BREAKING CHANGE : MAJOR
build(poetry): bump requests to v3
style(#32): add missing type annotations
git rebase -i
fix(roles): bug in admin role permissions
feat(ui): implement new button design
build(pip): upgrade package to remove vulnerabilities
refactor: file structure to improve code readability
perf(cli): rewrite methods
feat(api): endpoints to implement new customer dashboard
Personal Guide
<\u2013 Links \u2013>
Auto-generated with pylint-pyreverse
pylint-pyreverse
Full Size
calcipy.
configure_runtime_type_checking_mode()\n
Optionally configure runtime type checking mode globally.
def configure_runtime_type_checking_mode() -> None: # pragma: no cover\n \"\"\"Optionally configure runtime type checking mode globally.\"\"\"\n rtc_mode = _RuntimeTypeCheckingModes.from_environment()\n\n if rtc_mode is not _RuntimeTypeCheckingModes.OFF:\n from beartype.roar import BeartypeClawDecorWarning # noqa: PLC0415\n\n beartype_this_package(conf=BeartypeConf(\n warning_cls_on_decorator_exception=(\n None if rtc_mode is _RuntimeTypeCheckingModes.ERROR else BeartypeClawDecorWarning\n ),\n ))\n
Support can-skip logic from Make.
can_skip(*, prerequisites, targets)\n
Return true if the prerequisite files are have newer mtime than targets.
mtime
Example use with Invoke, but can be used anywhere:
@task()\ndef test(ctx: Context) -> None:\n if can_skip(prerequisites=[*Path('src').rglob('*.py')], targets=[Path('.coverage.xml')]):\n return # Exit early\n\n ... # Task code\n
@beartype\ndef can_skip(*, prerequisites: List[Path], targets: List[Path]) -> bool:\n \"\"\"Return true if the prerequisite files are have newer `mtime` than targets.\n\n Example use with Invoke, but can be used anywhere:\n\n ```py\n @task()\n def test(ctx: Context) -> None:\n if can_skip(prerequisites=[*Path('src').rglob('*.py')], targets=[Path('.coverage.xml')]):\n return # Exit early\n\n ... # Task code\n ```\n\n \"\"\"\n if not (ts_prerequisites := [pth.stat().st_mtime for pth in prerequisites]):\n raise ValueError('Required files do not exist', prerequisites)\n\n ts_targets = [pth.stat().st_mtime for pth in targets]\n if ts_targets and min(ts_targets) > max(ts_prerequisites):\n LOGGER.warning('Skipping because targets are newer', targets=targets)\n return True\n return False\n
dont_skip(*, prerequisites, targets)\n
To use for testing with mock; always returns False.
@beartype\ndef dont_skip(*, prerequisites: List[Path], targets: List[Path]) -> bool:\n \"\"\"To use for testing with mock; always returns False.\"\"\"\n LOGGER.debug('Mocking can_skip', prerequisites=prerequisites, targets=targets)\n return False\n
Extend Invoke for Calcipy.
Bases: Config
Config
Opinionated Config with better defaults.
class CalcipyConfig(Config):\n \"\"\"Opinionated Config with better defaults.\"\"\"\n\n @staticmethod\n def global_defaults() -> Dict: # type: ignore[type-arg] # pragma: no cover\n \"\"\"Override the global defaults.\"\"\"\n invoke_defaults = Config.global_defaults()\n calcipy_defaults = {\n 'run': {\n 'echo': True,\n 'echo_format': '\\033[2;3;37mRunning: {command}\\033[0m',\n 'pty': use_pty(),\n },\n }\n return merge_dicts(invoke_defaults, calcipy_defaults)\n
staticmethod
global_defaults()\n
Override the global defaults.
@staticmethod\ndef global_defaults() -> Dict: # type: ignore[type-arg] # pragma: no cover\n \"\"\"Override the global defaults.\"\"\"\n invoke_defaults = Config.global_defaults()\n calcipy_defaults = {\n 'run': {\n 'echo': True,\n 'echo_format': '\\033[2;3;37mRunning: {command}\\033[0m',\n 'pty': use_pty(),\n },\n }\n return merge_dicts(invoke_defaults, calcipy_defaults)\n
start_program(pkg_name, pkg_version, module=None, collection=None)\n
Run the customized Invoke Program.
FYI: recommendation is to extend the core_args method, but this won\u2019t parse positional arguments: https://docs.pyinvoke.org/en/stable/concepts/library.html#modifying-core-parser-arguments
core_args
@beartype\ndef start_program(\n pkg_name: str,\n pkg_version: str,\n module: Optional[ModuleType] = None,\n collection: Optional[Union[Collection, InvokeCollection]] = None,\n) -> None: # pragma: no cover\n \"\"\"Run the customized Invoke Program.\n\n FYI: recommendation is to extend the `core_args` method, but this won't parse positional arguments:\n https://docs.pyinvoke.org/en/stable/concepts/library.html#modifying-core-parser-arguments\n\n \"\"\"\n # Manipulate 'sys.argv' to hide arguments that invoke can't parse\n _gto = GlobalTaskOptions()\n sys_argv: List[str] = sys.argv[:1]\n last_argv = ''\n for argv in sys.argv[1:]:\n if not last_argv.startswith('-') and Path(argv).is_file():\n _gto.file_args.append(Path(argv))\n # Check for CLI flags\n elif argv in {'-v', '-vv', '-vvv', '--verbose'}:\n _gto.verbose = argv.count('v')\n elif argv == '--keep-going':\n _gto.keep_going = True\n # Check for CLI arguments with values\n elif last_argv == '--working-dir':\n _gto.working_dir = Path(argv).resolve()\n elif argv != '--working-dir':\n sys_argv.append(argv)\n last_argv = argv\n _gto.file_args = [\n _f if _f.is_absolute() else Path.cwd() / _f\n for _f in _gto.file_args\n ]\n sys.argv = sys_argv\n\n class _CalcipyConfig(CalcipyConfig):\n\n gto: GlobalTaskOptions = _gto\n\n if module and collection:\n raise ValueError('Only one of collection or module can be specified')\n\n _CalcipyProgram(\n name=pkg_name,\n version=pkg_version,\n namespace=Collection.from_module(module) if module else collection,\n config_class=_CalcipyConfig,\n ).run()\n
task(*dec_args, **dec_kwargs)\n
Mark wrapped callable object as a valid Invoke task.
def task(*dec_args: Any, **dec_kwargs: Any) -> Callable: # type: ignore[type-arg]\n \"\"\"Mark wrapped callable object as a valid Invoke task.\"\"\"\n def wrapper(func: Any) -> Callable: # type: ignore[type-arg]\n # Attach arguments for Task\n setattr(func, TASK_ARGS_ATTR, dec_args)\n setattr(func, TASK_KWARGS_ATTR, dec_kwargs)\n # Attach public attributes from invoke that are expected\n func.help = dec_kwargs.pop('help', {})\n\n def _with_kwargs(**extra_kwargs: Any) -> Callable: # type: ignore[type-arg] # nosem\n \"\"\"Support building partial tasks.\"\"\"\n if extra_kwargs:\n # Set a unique name when 'extra_kwargs' was provided\n # https://github.com/pyinvoke/invoke/blob/07b836f2663bb073a7bcef3d6c454e1dc6b867ae/invoke/tasks.py#L81-L104\n encoded = b64encode(str(extra_kwargs).encode())\n func.__name__ = f'{func.__name__}_{encoded.decode().rstrip(\"=\")}'\n\n @wraps(func) # nosem\n def _with_kwargs_inner(*args: Any, **kwargs: Any) -> Any:\n return func(*args, **kwargs, **extra_kwargs)\n return _with_kwargs_inner\n\n func.with_kwargs = _with_kwargs\n\n @wraps(func) # nosem\n def _inner(*args: Any, **kwargs: Any) -> Any:\n return func(*args, **kwargs)\n\n return _inner\n\n # Handle the case when the decorator is called without arguments\n if (\n len(dec_args) == 1\n and callable(dec_args[0])\n and not hasattr(dec_args[0], TASK_ARGS_ATTR)\n ):\n return wrapper(dec_args[0])\n\n return wrapper\n
Find Files.
find_project_files(path_project, ignore_patterns)\n
Find project files in git version control.
Note: uses the relative project directory and verifies that each file exists
path_project: Path to the project directory\nignore_patterns: glob ignore patterns\n
Dict[str, List[Path]]: where keys are the suffix (without leading dot) and values the list of paths\n
@beartype\ndef find_project_files(path_project: Path, ignore_patterns: List[str]) -> List[Path]:\n \"\"\"Find project files in git version control.\n\n > Note: uses the relative project directory and verifies that each file exists\n\n Args:\n ----\n path_project: Path to the project directory\n ignore_patterns: glob ignore patterns\n\n Returns:\n -------\n Dict[str, List[Path]]: where keys are the suffix (without leading dot) and values the list of paths\n\n \"\"\"\n file_paths = []\n rel_filepaths = _get_all_files(cwd=path_project)\n filtered_rel_files = _filter_files(rel_filepaths=rel_filepaths, ignore_patterns=ignore_patterns)\n for rel_file in filtered_rel_files:\n path_file = path_project / rel_file\n if path_file.is_file():\n file_paths.append(path_file)\n else: # pragma: no cover\n LOGGER.warning('Could not find the specified file', path_file=path_file)\n return file_paths\n
find_project_files_by_suffix(path_project, *, ignore_patterns=None)\n
@beartype\ndef find_project_files_by_suffix(\n path_project: Path, *, ignore_patterns: Optional[List[str]] = None,\n) -> Dict[str, List[Path]]:\n \"\"\"Find project files in git version control.\n\n > Note: uses the relative project directory and verifies that each file exists\n\n Args:\n ----\n path_project: Path to the project directory\n ignore_patterns: glob ignore patterns\n\n Returns:\n -------\n Dict[str, List[Path]]: where keys are the suffix (without leading dot) and values the list of paths\n\n \"\"\"\n file_lookup: Dict[str, List[Path]] = defaultdict(list)\n for path_file in find_project_files(path_project, ignore_patterns or []):\n file_lookup[path_file.suffix.lstrip('.')].append(path_file)\n return dict(file_lookup)\n
Invoke Helpers.
get_doc_subdir(path_project=None)\n
Retrieve the documentation directory from the copier answer file.
path_project: Path to the project directory with contains `.copier-answers.yml`\n
Path: to the source documentation directory\n
@beartype\ndef get_doc_subdir(path_project: Optional[Path] = None) -> Path:\n \"\"\"Retrieve the documentation directory from the copier answer file.\n\n Args:\n ----\n path_project: Path to the project directory with contains `.copier-answers.yml`\n\n Returns:\n -------\n Path: to the source documentation directory\n\n \"\"\"\n path_copier = (path_project or get_project_path()) / COPIER_ANSWERS\n doc_dir = read_yaml_file(path_copier).get('doc_dir', 'docs')\n return path_copier.parent / doc_dir / 'docs'\n
cached
get_project_path()\n
Retrieve the cwd.
cwd
@lru_cache(maxsize=1)\ndef get_project_path() -> Path:\n \"\"\"Retrieve the `cwd`.\"\"\"\n return Path.cwd()\n
run(ctx, *run_args, **run_kwargs)\n
Wrap invoke.run to run within the working_dir.
working_dir
@beartype\ndef run(ctx: Context, *run_args: Any, **run_kwargs: Any) -> Optional[Result]:\n \"\"\"Wrap invoke.run to run within the `working_dir`.\"\"\"\n working_dir = '.'\n with suppress(AttributeError):\n working_dir = ctx.config.gto.working_dir\n\n with ctx.cd(working_dir):\n return ctx.run(*run_args, **run_kwargs)\n
use_pty()\n
Return False on Windows and some CI environments.
@lru_cache(maxsize=1)\ndef use_pty() -> bool:\n \"\"\"Return False on Windows and some CI environments.\"\"\"\n if platform.system() == 'Windows':\n return False\n return not environ.get('GITHUB_ACTION')\n
Start the command line program.
start()\n
def start() -> None: # pragma: no cover\n \"\"\"Run the customized Invoke Program.\"\"\"\n from .tasks import all_tasks # noqa: PLC0415\n start_program(__pkg_name__, __version__, all_tasks)\n
start_docs()\n
Run CLI with only the cl and doc namespaces.
def start_docs() -> None: # pragma: no cover\n \"\"\"Run CLI with only the cl and doc namespaces.\"\"\"\n from .tasks import cl, doc # noqa: PLC0415\n _start_subset([cl, doc])\n
start_lint()\n
Run CLI with only the lint namespace.
def start_lint() -> None: # pragma: no cover\n \"\"\"Run CLI with only the lint namespace.\"\"\"\n from .tasks import lint # noqa: PLC0415\n _start_subset([lint])\n
start_pack()\n
Run CLI with only the pack namespace.
def start_pack() -> None: # pragma: no cover\n \"\"\"Run CLI with only the pack namespace.\"\"\"\n from .tasks import pack # noqa: PLC0415\n _start_subset([pack])\n
start_tags()\n
Run CLI with only the tags namespace.
def start_tags() -> None: # pragma: no cover\n \"\"\"Run CLI with only the tags namespace.\"\"\"\n from .tasks import tags # noqa: PLC0415\n _start_subset([tags])\n
start_test()\n
Run CLI with only the test namespace.
def start_test() -> None: # pragma: no cover\n \"\"\"Run CLI with only the test namespace.\"\"\"\n from .tasks import test # noqa: PLC0415\n _start_subset([test])\n
start_types()\n
Run CLI with only the types namespace.
def start_types() -> None: # pragma: no cover\n \"\"\"Run CLI with only the types namespace.\"\"\"\n from .tasks import types # noqa: PLC0415\n _start_subset([types])\n
check_for_stale_packages(*, stale_months, path_lock=LOCK, path_cache=CALCIPY_CACHE)\n
Check for stale packages by reading from the cache, and updating if necessary.
stale_months: cutoff in months for when a package might be stale enough to be a risk\npath_lock: path to poetry lock file\npath_cache: path to calcipy package cache file\n
@beartype\ndef check_for_stale_packages(*, stale_months: int, path_lock: Path = LOCK, path_cache: Path = CALCIPY_CACHE) -> bool:\n \"\"\"Check for stale packages by reading from the cache, and updating if necessary.\n\n Args:\n ----\n stale_months: cutoff in months for when a package might be stale enough to be a risk\n path_lock: path to poetry lock file\n path_cache: path to calcipy package cache file\n\n \"\"\"\n packages = _read_packages(path_lock)\n cached_packages = _read_cache(path_cache)\n if cached_packages and can_skip.can_skip(prerequisites=[path_lock], targets=[path_cache]):\n packages = [*cached_packages.values()]\n else:\n packages = _collect_release_dates(packages, cached_packages)\n _write_cache(packages, path_cache)\n return _packages_are_stale(packages, stale_months=stale_months)\n
Check for stale packages.
module-attribute
CALCIPY_CACHE = Path('.calcipy_packaging.lock')\n
Path to the packaging lock file.
write_code_tag_file(\n *,\n path_tag_summary,\n paths_source,\n base_dir,\n regex=\"\",\n tags=\"\",\n header=\"# Task Summary\\n\\nAuto-Generated by `calcipy`\"\n)\n
Create the code tag summary file.
path_tag_summary: Path to the output file\npaths_source: list of source files to parse\nbase_dir: base directory relative to the searched files\nregex: compiled regular expression. Expected to have matching groups `(tag, text)`.\n Default is CODE_TAG_RE with tags from tag_order\ntags: subset of all tags to include in the report and specified order. Default is COMMON_CODE_TAGS\nheader: header text\n
@beartype\ndef write_code_tag_file(\n *,\n path_tag_summary: Path,\n paths_source: List[Path],\n base_dir: Path,\n regex: str = '',\n tags: str = '',\n header: str = '# Task Summary\\n\\nAuto-Generated by `calcipy`',\n) -> None:\n \"\"\"Create the code tag summary file.\n\n Args:\n ----\n path_tag_summary: Path to the output file\n paths_source: list of source files to parse\n base_dir: base directory relative to the searched files\n regex: compiled regular expression. Expected to have matching groups `(tag, text)`.\n Default is CODE_TAG_RE with tags from tag_order\n tags: subset of all tags to include in the report and specified order. Default is COMMON_CODE_TAGS\n header: header text\n\n \"\"\"\n tag_order = [_t.strip() for _t in tags.split(',') if _t] or COMMON_CODE_TAGS\n matcher = (regex or CODE_TAG_RE).format(tag='|'.join(tag_order))\n\n matches = _search_files(paths_source, re.compile(matcher))\n if report := _format_report(\n base_dir, matches, tag_order=tag_order,\n ).strip():\n path_tag_summary.parent.mkdir(exist_ok=True, parents=True)\n path_tag_summary.write_text(f'{header}\\n\\n{report}\\n\\n<!-- {SKIP_PHRASE} -->\\n')\n LOGGER.text('Created Code Tag Summary', path_tag_summary=path_tag_summary)\n elif path_tag_summary.is_file():\n path_tag_summary.unlink()\n
Collect code tags and output for review in a single location.
CODE_TAG_RE = '((^|\\\\s|\\\\(|\"|\\\\\\')(?P<tag>{tag})(:| -)([^\\\\r\\\\n]))(?P<text>.+)'\n
Default code tag regex with tag and text matching groups.
tag
text
Requires formatting with list of tags: CODE_TAG_RE.format(tag='|'.join(tag_list))
CODE_TAG_RE.format(tag='|'.join(tag_list))
Commonly, the tag_list could be COMMON_CODE_TAGS
tag_list
COMMON_CODE_TAGS
COMMON_CODE_TAGS = ['FIXME', 'TODO', 'PLANNED', 'HACK', 'REVIEW', 'TBD', 'DEBUG']\n
Most common code tags.
FYI and NOTE are excluded to not be tracked in the Code Summary.
SKIP_PHRASE = 'calcipy_skip_tags'\n
String that indicates the file should be excluded from the tag search.
github_blame_url(clone_uri)\n
Format the blame URL.
clone_uri: git remote URI\n
str: repo_url
repo_url
@beartype\ndef github_blame_url(clone_uri: str) -> str:\n \"\"\"Format the blame URL.\n\n Args:\n ----\n clone_uri: git remote URI\n\n Returns:\n -------\n str: `repo_url`\n\n \"\"\"\n # Could be ssh or http (with or without .git)\n # > git@github.com:KyleKing/calcipy.git\n # > https://github.com/KyleKing/calcipy.git\n if matches := re.compile(_GITHUB_ORIGIN).match(clone_uri):\n github_url = 'https://github.com/'\n return f\"{github_url}{matches['owner']}/{matches['repository']}\"\n return ''\n
ddict(**kwargs)\n
Return a dotted dictionary that can also be accessed normally.
python-box
cleverdict
munch
bunch
ddict
**kwargs: keyword arguments formatted into dictionary\n
DdictType: dotted dictionary\n
@beartype\ndef ddict(**kwargs: Dict[str, Any]) -> DdictType:\n \"\"\"Return a dotted dictionary that can also be accessed normally.\n\n - Currently uses `python-box`\n - Could consider `cleverdict` which had updates as recently as 2022\n - There are numerous other variations that haven't been updated since 2020, such as `munch`, `bunch`, `ddict`\n\n Args:\n ----\n **kwargs: keyword arguments formatted into dictionary\n\n Returns:\n -------\n DdictType: dotted dictionary\n\n \"\"\"\n return Box(kwargs)\n
Dotted dictionary for consistent interface.
Consider moving to Corallium, but I don\u2019t have any uses for it yet.
DdictType = Union[Dict[str, Any], Box]\n
Return type from ddict().
ddict()
Experiment with bumping the git tag using griffe.
griffe
bump_tag(*, pkg_name, tag, tag_prefix)\n
Make a SemVer minor bump using griffe if there were any breaking changes.
Major versions must be bumped manually
@beartype\ndef bump_tag(*, pkg_name: str, tag: str, tag_prefix: str) -> str:\n \"\"\"Make a SemVer minor bump using `griffe` if there were any breaking changes.\n\n Major versions must be bumped manually\n\n \"\"\"\n previous = griffe.load_git(pkg_name, ref=tag)\n current = griffe.load(pkg_name)\n\n breakages = [*griffe.find_breaking_changes(previous, current)]\n for breakage in breakages:\n try:\n LOGGER.text(breakage._explain_oneline()) # noqa: SLF001\n except BuiltinModuleError: # noqa: PERF203\n LOGGER.warning(str(breakage))\n except Exception:\n LOGGER.exception(str(breakage))\n\n try:\n ver = semver.Version.parse(tag.replace(tag_prefix, ''))\n except ValueError:\n LOGGER.exception('Failed to parse tag', tag=tag)\n return ''\n new_ver = ver.bump_minor() if any(breakages) else ver.bump_patch()\n return f'{tag_prefix}{new_ver}'\n
Experiment with checking for duplicate test names.
run(test_path)\n
Check for duplicates in the test suite.
Inspired by: https://stackoverflow.com/a/67840804/3219667
@beartype\ndef run(test_path: Path) -> List[str]: # noqa: C901 # pylint: disable=too-complex\n \"\"\"Check for duplicates in the test suite.\n\n Inspired by: https://stackoverflow.com/a/67840804/3219667\n\n \"\"\"\n summary = set()\n duplicates = []\n\n for path_test in test_path.rglob('test_*.py'): # pylint: disable=too-many-nested-blocks\n LOGGER.info(path_test.as_posix())\n parsed_ast = ast.parse(path_test.read_text())\n\n for node in parsed_ast.body:\n if isinstance(node, ast.FunctionDef):\n if node.name in summary and node.name.startswith('test_'):\n duplicates.append(node.name)\n summary.add(node.name)\n _show_info(node)\n elif isinstance(node, ast.ClassDef):\n LOGGER.info('Class name', name=node.name)\n for method in node.body:\n if isinstance(method, ast.FunctionDef):\n _show_info(method)\n\n for node in ast.walk(parsed_ast): # type: ignore[assignment]\n if (\n isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))\n and node.name not in summary\n ):\n LOGGER.info('Found new function(s) through walking')\n _show_info(node)\n summary.add(node.name)\n\n if duplicates:\n LOGGER.error('Found Duplicates', duplicates=duplicates)\n return duplicates\n
write_autoformatted_md_sections(handler_lookup=None, paths_md=None)\n
Populate the auto-formatted sections of markdown files with user-configured logic.
@beartype\ndef write_autoformatted_md_sections(\n handler_lookup: Optional[HandlerLookupT] = None,\n paths_md: Optional[List[Path]] = None,\n) -> None:\n \"\"\"Populate the auto-formatted sections of markdown files with user-configured logic.\"\"\"\n _lookup: HandlerLookupT = handler_lookup or {\n 'COVERAGE ': _handle_coverage,\n 'SOURCE_FILE=': _handle_source_file,\n }\n\n paths = paths_md or find_project_files_by_suffix(get_project_path()).get('md') or []\n for path_md in paths:\n LOGGER.text_debug('Processing', path_md=path_md)\n if md_lines := _ReplacementMachine().parse(read_lines(path_md), _lookup, path_md):\n path_md.write_text('\\n'.join(md_lines) + '\\n', encoding='utf-8')\n
Markdown Machine.
HandlerLookupT = Dict[str, Callable[[str, Path], List[str]]]\n
Handler Lookup.
build_check(session)\n
Check that the built output meets all checks.
@nox_poetry_session(python=_get_pythons()[-1:], reuse_venv=True)\ndef build_check(session: NPSession) -> None: # pragma: no cover\n \"\"\"Check that the built output meets all checks.\"\"\"\n # Build sdist and fix return URI, which will have file://...#egg=calcipy\n sdist_uri = session.poetry.build_package(distribution_format=DistributionFormat.SDIST)\n path_sdist = Path(url2pathname(urlparse(sdist_uri).path))\n LOGGER.text_debug('Fixed sdist URI', sdist_uri=sdist_uri, path_sdist=path_sdist)\n # Check with pyroma\n session.install('pyroma>=4.0', '--upgrade')\n # required for \"poetry.core.masonry.api\" build backend\n session.run('python', '-m', 'pip', 'install', 'poetry>=1.3', stdout=True)\n session.run('pyroma', '--file', path_sdist.as_posix(), '--min=9', stdout=True)\n
build_dist(session)\n
Build and test the project files within a controlled environment for repeatability.
@nox_session(python=_get_pythons()[-1:], reuse_venv=False)\ndef build_dist(session: Union[NoxSession, NPSession]) -> None: # pragma: no cover\n \"\"\"Build and test the project files within a controlled environment for repeatability.\"\"\"\n dist_path = Path('dist')\n if_found_unlink(dist_path)\n\n # Support 'corallium' by re-implementing \"session.poetry.build_package()\", from:\n # https://github.com/cjolowicz/nox-poetry/blob/5772b66ebff8d5a3351a08ed402d3d31e48be5f8/src/nox_poetry/sessions.py#L233-L255\n # https://github.com/cjolowicz/nox-poetry/blob/5772b66ebff8d5a3351a08ed402d3d31e48be5f8/src/nox_poetry/poetry.py#L111-L154\n output = session.run(*shlex.split('poetry build --format=wheel --no-ansi'),\n external=True, silent=True)\n output = cast(str, output)\n wheel = dist_path / output.split()[-1]\n path_wheel = wheel.resolve().as_uri()\n\n LOGGER.text('Created wheel', path_wheel=path_wheel)\n # Install the wheel and check that imports without any of the optional dependencies\n session.install(path_wheel)\n session.run(*shlex.split('python scripts/check_imports.py'), stdout=True)\n
tests(session)\n
Run doit test task for specified python versions.
@nox_session(python=_get_pythons(), reuse_venv=True)\ndef tests(session: Union[NoxSession, NPSession]) -> None: # pragma: no cover\n \"\"\"Run doit test task for specified python versions.\"\"\"\n _install_local(session, ['ddict', 'doc', 'lint', 'nox', 'stale', 'tags', 'test'])\n session.run(*shlex.split('pytest ./tests'), stdout=True, env={'RUNTIME_TYPE_CHECKING_MODE': 'WARNING'})\n
nox-poetry configuration file.
Useful snippets from docs
poetry run nox -l\npoetry run nox --list-sessions\n\npoetry run nox -s build_check-3.8 build_dist-3.8 tests-3.8\npoetry run nox --session tests-3.11\n\npoetry run nox --python 3.8\n\npoetry run nox -k \"not build_check and not build_dist\"\n
Useful nox snippets
# Example conditionally skipping a session\nif not session.interactive:\n session.skip('Cannot run detect-secrets audit in non-interactive shell')\n\n# Install pinned version\nsession.install('detect-secrets==1.0.3')\n\n# Example capturing STDOUT into a file (could do the same for stderr)\npath_stdout = Path('.stdout.txt').resolve()\nwith open(path_stdout, 'w') as out:\n session.run(*shlex.split('echo Hello World!'), stdout=out)\n
Bases: Collection
Collection
class Collection(InvokeCollection):\n\n @classmethod\n def from_module(\n cls,\n module: ModuleType,\n name: Optional[str] = None,\n config: Optional[Dict[str, Any]] = None,\n loaded_from: Optional[str] = None,\n auto_dash_names: Optional[bool] = None,\n ) -> 'InvokeCollection':\n \"\"\"Extend search for a namespace, Task, or deferred task.\"\"\"\n collection = super().from_module(\n module=module,\n name=name,\n config=config,\n loaded_from=loaded_from,\n auto_dash_names=auto_dash_names,\n )\n\n # If tasks were not loaded from a namespace or otherwise found\n if not collection.task_names:\n # Look for any decorated, but deferred \"Tasks\"\n for task in (fxn for fxn in vars(module).values() if hasattr(fxn, TASK_ARGS_ATTR)):\n collection.add_task(task)\n\n return collection\n\n def add_task(\n self,\n task: DeferedTask,\n name: Optional[str] = None,\n aliases: Optional[Tuple[str, ...]] = None,\n default: Optional[bool] = None,\n ) -> None:\n \"\"\"Extend for deferred tasks.\"\"\"\n super().add_task(task=_build_task(task), name=name, aliases=aliases, default=default)\n
add_task(task, name=None, aliases=None, default=None)\n
Extend for deferred tasks.
def add_task(\n self,\n task: DeferedTask,\n name: Optional[str] = None,\n aliases: Optional[Tuple[str, ...]] = None,\n default: Optional[bool] = None,\n) -> None:\n \"\"\"Extend for deferred tasks.\"\"\"\n super().add_task(task=_build_task(task), name=name, aliases=aliases, default=default)\n
classmethod
from_module(module, name=None, config=None, loaded_from=None, auto_dash_names=None)\n
Extend search for a namespace, Task, or deferred task.
@classmethod\ndef from_module(\n cls,\n module: ModuleType,\n name: Optional[str] = None,\n config: Optional[Dict[str, Any]] = None,\n loaded_from: Optional[str] = None,\n auto_dash_names: Optional[bool] = None,\n) -> 'InvokeCollection':\n \"\"\"Extend search for a namespace, Task, or deferred task.\"\"\"\n collection = super().from_module(\n module=module,\n name=name,\n config=config,\n loaded_from=loaded_from,\n auto_dash_names=auto_dash_names,\n )\n\n # If tasks were not loaded from a namespace or otherwise found\n if not collection.task_names:\n # Look for any decorated, but deferred \"Tasks\"\n for task in (fxn for fxn in vars(module).values() if hasattr(fxn, TASK_ARGS_ATTR)):\n collection.add_task(task)\n\n return collection\n
Bases: BaseModel
BaseModel
Global Task Options.
class GlobalTaskOptions(BaseModel):\n \"\"\"Global Task Options.\"\"\"\n\n working_dir: Path = Field(default_factory=Path.cwd)\n \"\"\"Working directory for the program to use globally.\"\"\"\n\n file_args: List[Path] = Field(default_factory=list)\n \"\"\"List of Paths to modify.\"\"\"\n\n verbose: PositiveInt = Field(default=0, le=3)\n \"\"\"Verbosity level.\"\"\"\n\n keep_going: bool = False\n \"\"\"Continue task execution regardless of failure.\"\"\"\n
class-attribute
instance-attribute
file_args = Field(default_factory=list)\n
List of Paths to modify.
keep_going = False\n
Continue task execution regardless of failure.
verbose = Field(default=0, le=3)\n
Verbosity level.
working_dir = Field(default_factory=cwd)\n
Working directory for the program to use globally.
Tasks can be imported piecemeal or imported in their entirety from here.
TaskList = List[Union[Call, DeferedTask]]\n
List of wrapped or normal task functions.
main(_ctx)\n
Run main task pipeline.
@task(post=with_progress(_MAIN_TASKS))\ndef main(_ctx: Context) -> None:\n \"\"\"Run main task pipeline.\"\"\"\n
other(_ctx)\n
Run tasks that are otherwise not exercised in main.
@task(post=with_progress(_OTHER_TASKS))\ndef other(_ctx: Context) -> None:\n \"\"\"Run tasks that are otherwise not exercised in main.\"\"\"\n
progress(_ctx, *, index, total)\n
Progress Task.
@task(\n help={\n 'index': 'Current index (0-indexed)',\n 'total': 'Total steps',\n },\n show_task_info=False,\n)\ndef progress(_ctx: Context, *, index: int, total: int) -> None:\n \"\"\"Progress Task.\"\"\"\n LOGGER.text('Progress', is_header=True, index=index + 1, total=total)\n
release(ctx, *, suffix=None)\n
Run release pipeline.
@task(\n help=cl.bump.help, # pyright: ignore[reportFunctionMemberAccess]\n post=with_progress(\n [\n pack.lock,\n doc.build,\n doc.deploy,\n pack.publish,\n ],\n offset=1,\n ),\n)\ndef release(ctx: Context, *, suffix: cl.SuffixT = None) -> None:\n \"\"\"Run release pipeline.\"\"\"\n cl.bumpz(ctx, suffix=suffix)\n
summary(_ctx, *, message)\n
Summary Task.
@task(\n help={\n 'message': 'String message to display',\n },\n show_task_info=False,\n)\ndef summary(_ctx: Context, *, message: str) -> None:\n \"\"\"Summary Task.\"\"\"\n LOGGER.text(message, is_header=True)\n
with_progress(items, offset=0)\n
Inject intermediary \u2018progress\u2019 tasks.
items: list of tasks\noffset: Optional offset to shift counters\n
@beartype\ndef with_progress(items: Any, offset: int = 0) -> TaskList:\n \"\"\"Inject intermediary 'progress' tasks.\n\n Args:\n ----\n items: list of tasks\n offset: Optional offset to shift counters\n\n \"\"\"\n task_items = [_build_task(_t) for _t in items]\n message = 'Running tasks: ' + ', '.join([str(_t.__name__) for _t in task_items])\n tasks: TaskList = [summary.with_kwargs(message=message)] # pyright: ignore[reportFunctionMemberAccess]\n\n total = len(task_items) + offset\n for idx, item in enumerate(task_items):\n tasks += [\n progress.with_kwargs(index=idx + offset, total=total), # pyright: ignore[reportFunctionMemberAccess]\n item,\n ]\n return tasks\n
Changelog CLI.
SuffixT = Optional[Literal['alpha', 'beta', 'rc']]\n
Prerelease Suffix Type.
bump(ctx, *, suffix=None)\n
Bumps project version based on commits & settings in pyproject.toml.
@task(\n pre=[write],\n help={\n 'suffix': 'Specify prerelease suffix for version bump (alpha, beta, rc)',\n },\n)\ndef bump(ctx: Context, *, suffix: SuffixT = None) -> None:\n \"\"\"Bumps project version based on commits & settings in pyproject.toml.\"\"\"\n bumpz(ctx, suffix=suffix)\n
bumpz(ctx, *, suffix=None)\n
@beartype\ndef bumpz(ctx: Context, *, suffix: SuffixT = None) -> None:\n \"\"\"Bumps project version based on commits & settings in pyproject.toml.\"\"\"\n check_installed(ctx, executable='gh', message=GH_MESSAGE)\n\n opt_cz_args = f' --prerelease={suffix}' if suffix else ''\n run(ctx, f'{python_dir()}/cz bump{opt_cz_args} --annotated-tag --no-verify --gpg-sign')\n\n run(ctx, 'git push origin --tags --no-verify')\n\n get_last_tag = 'git tag --list --sort=-creatordate | head -n 1'\n opt_gh_args = ' --prerelease' if suffix else ''\n run(ctx, f'gh release create --generate-notes $({get_last_tag}){opt_gh_args}')\n
write(ctx)\n
Write a Changelog file with the raw Git history.
Resources:
List[DoitAction]: doit actions\n
@task()\ndef write(ctx: Context) -> None:\n \"\"\"Write a Changelog file with the raw Git history.\n\n Resources:\n\n - https://keepachangelog.com/en/1.0.0/\n - https://www.conventionalcommits.org/en/v1.0.0/\n - https://writingfordevelopers.substack.com/p/how-to-write-a-commit-message\n - https://chris.beams.io/posts/git-commit/\n - https://semver.org/\n - https://calver.org/\n\n Returns\n -------\n List[DoitAction]: doit actions\n\n \"\"\"\n run(ctx, f'{python_dir()}/cz changelog')\n path_cl = get_project_path() / 'CHANGELOG.md'\n if not path_cl.is_file():\n msg = f'Could not locate the changelog at: {path_cl}'\n raise FileNotFoundError(msg)\n path_cl.replace(get_doc_subdir() / path_cl.name)\n
Calcipy-Invoke Defaults.
from_ctx(ctx, group, key)\n
Safely extract the value from the context or the defaults.
Instead of ctx.tests.out_dir use from_ctx(ctx, 'test', 'out_dir')
ctx.tests.out_dir
from_ctx(ctx, 'test', 'out_dir')
@beartype\ndef from_ctx(ctx: Context, group: str, key: str) -> str:\n \"\"\"Safely extract the value from the context or the defaults.\n\n Instead of `ctx.tests.out_dir` use `from_ctx(ctx, 'test', 'out_dir')`\n\n \"\"\"\n with suppress(KeyError):\n return str(ctx.config[group][key])\n return str(DEFAULTS[group][key])\n
new_collection()\n
Initialize a collection with the combination of merged and project-specific defaults.
@beartype\ndef new_collection() -> Collection:\n \"\"\"Initialize a collection with the combination of merged and project-specific defaults.\"\"\"\n ns = Collection('')\n\n # Merge default and user configuration\n ns.configure(DEFAULTS)\n config_path = Path('.calcipy.json')\n if config_path.is_file():\n ns.configure(json.loads(config_path.read_text(encoding='utf-8')))\n\n return ns\n
Document CLI.
build(ctx)\n
Build documentation with mkdocs.
@task()\ndef build(ctx: Context) -> None:\n \"\"\"Build documentation with mkdocs.\"\"\"\n auto_doc_path = get_doc_subdir().parent / 'modules'\n write_autoformatted_md_sections()\n delete_dir(auto_doc_path)\n _diagram_task(ctx, auto_doc_path)\n\n # Find and trim trailing whitespace\n for path_md in auto_doc_path.rglob('*.md'):\n trim_trailing_whitespace(path_md)\n\n run(ctx, f'{python_dir()}/mkdocs build --site-dir {get_out_dir()}')\n
deploy(ctx)\n
Deploy docs to the Github gh-pages branch.
gh-pages
@task()\ndef deploy(ctx: Context) -> None:\n \"\"\"Deploy docs to the Github `gh-pages` branch.\"\"\"\n if _is_mkdocs_local(): # pragma: no cover\n raise NotImplementedError('Not yet configured to deploy documentation without \"use_directory_urls\"')\n\n with suppress(UnexpectedExit):\n run(ctx, 'pre-commit uninstall') # To prevent pre-commit failures when mkdocs calls push\n run(ctx, f'{python_dir()}/mkdocs gh-deploy --force')\n with suppress(UnexpectedExit):\n run(ctx, 'pre-commit install') # Restore pre-commit\n
get_out_dir()\n
Retrieve the mkdocs-specified site directory.
@beartype\ndef get_out_dir() -> Path:\n \"\"\"Retrieve the mkdocs-specified site directory.\"\"\"\n mkdocs_config = read_yaml_file(get_project_path() / MKDOCS_CONFIG)\n return Path(mkdocs_config.get('site_dir', 'releases/site'))\n
watch(ctx)\n
Serve local documentation for local editing.
@task()\ndef watch(ctx: Context) -> None:\n \"\"\"Serve local documentation for local editing.\"\"\"\n if _is_mkdocs_local(): # pragma: no cover\n path_doc_index = get_out_dir() / 'index.html'\n open_in_browser(path_doc_index)\n else: # pragma: no cover\n webbrowser.open('http://localhost:8000')\n run(ctx, f'{python_dir()}/mkdocs serve --dirtyreload')\n
Utilities for working in calcipy\u2019s python environment.
check_installed(ctx, executable, message)\n
If the required executable isn\u2019t present, raise a clear user error.
@beartype\ndef check_installed(ctx: Context, executable: str, message: str) -> None:\n \"\"\"If the required executable isn't present, raise a clear user error.\"\"\"\n res = run(ctx, f'which {executable}', warn=True, hide=True)\n if not res or res.exited == 1:\n raise RuntimeError(message)\n
python_dir()\n
Run an executable from the currently active Python directory.
@lru_cache(maxsize=1)\ndef python_dir() -> str:\n \"\"\"Run an executable from the currently active Python directory.\"\"\"\n return str(resolve_python().parent)\n
python_m()\n
Return the active python path and -m flag.
-m
@lru_cache(maxsize=1)\ndef python_m() -> str:\n \"\"\"Return the active python path and `-m` flag.\"\"\"\n return f'{resolve_python()} -m'\n
resolve_python()\n
Resolve the user\u2019s Python path based on sys.
sys
@lru_cache(maxsize=1)\ndef resolve_python() -> Path:\n \"\"\"Resolve the user's Python path based on `sys`.\"\"\"\n python_path = Path(sys.executable)\n with suppress(ValueError):\n return python_path.relative_to(Path.cwd())\n return python_path\n
Lint CLI.
check(ctx)\n
Run ruff as check-only.
@task(default=True)\ndef check(ctx: Context) -> None:\n \"\"\"Run ruff as check-only.\"\"\"\n _inner_task(ctx, command='ruff check')\n
fix(ctx, *, unsafe=False)\n
Run ruff and apply fixes.
@task(\n help={\n 'unsafe': 'if provided, attempt even fixes considered unsafe',\n },\n)\ndef fix(ctx: Context, *, unsafe: bool = False) -> None:\n \"\"\"Run ruff and apply fixes.\"\"\"\n cli_args = '--fix'\n if unsafe:\n cli_args += ' --unsafe-fixes'\n _inner_task(ctx, command='ruff check', cli_args=cli_args)\n
pre_commit(ctx, *, no_update=False)\n
Run pre-commit.
@task(\n help={\n 'no_update': 'Skip updating the pre-commit hooks',\n },\n)\ndef pre_commit(ctx: Context, *, no_update: bool = False) -> None:\n \"\"\"Run pre-commit.\"\"\"\n check_installed(ctx, executable='pre-commit', message=PRE_COMMIT_MESSAGE)\n\n run(ctx, 'pre-commit install')\n if not no_update:\n run(ctx, 'pre-commit autoupdate')\n\n stages_cli = ' '.join(f'--hook-stage {stg}' for stg in ALL_PRE_COMMIT_HOOK_STAGES)\n run(ctx, f'pre-commit run --all-files {stages_cli}')\n
pylint(ctx, *, report=False)\n
Run pylint.
@task(\n help={\n 'report': 'if provided, show the pylint summary report',\n },\n)\ndef pylint(ctx: Context, *, report: bool = False) -> None:\n \"\"\"Run pylint.\"\"\"\n cli_args = '--report=y' if report else ''\n _inner_task(ctx, command='pylint', cli_args=cli_args)\n
@task()\ndef watch(ctx: Context) -> None:\n \"\"\"Run ruff as check-only.\"\"\"\n _inner_task(ctx, command='ruff check', cli_args='--watch --show-source')\n
Nox CLI.
noxfile(ctx, *, session='')\n
Run nox from the local noxfile.
@task(\n default=True,\n help={\n 'session': 'Optional session to run',\n },\n)\ndef noxfile(ctx: Context, *, session: str = '') -> None:\n \"\"\"Run nox from the local noxfile.\"\"\"\n cli_args = ['--session', session] if session else []\n run(ctx, f'{python_dir()}/nox --error-on-missing-interpreters {\" \".join(cli_args)}')\n
Packaging CLI.
bump_tag(ctx, *, tag, tag_prefix='', pkg_name='')\n
Example for calcipy:
./run pack.bump-tag --tag=\"$(git tag -l \"*\" | sort | head -n 5 | tail -n 1)\" --tag-prefix=\"\"\n
@task(\n help={\n 'tag': 'Last tag, can be provided with `--tag=\"$(git tag -l \"v*\" | sort | tail -n 1)\"`',\n 'tag_prefix': 'Optional tag prefix, such as \"v\"',\n 'pkg_name': 'Optional package name. If not provided, will read the poetry pyproject.toml file',\n },\n)\ndef bump_tag(ctx: Context, *, tag: str, tag_prefix: str = '', pkg_name: str = '') -> None: # noqa: ARG001\n \"\"\"Experiment with bumping the git tag using `griffe`.\n\n Example for `calcipy`:\n\n ```sh\n ./run pack.bump-tag --tag=\"$(git tag -l \"*\" | sort | head -n 5 | tail -n 1)\" --tag-prefix=\"\"\n ```\n\n \"\"\"\n if not tag:\n raise ValueError('tag must not be empty')\n if not pkg_name:\n poetry_config = file_helpers.read_pyproject()['tool']['poetry']\n pkg_name = poetry_config['name']\n\n from calcipy.experiments import bump_programmatically # noqa: PLC0415\n\n new_version = bump_programmatically.bump_tag(\n pkg_name=pkg_name,\n tag=tag,\n tag_prefix=tag_prefix,\n )\n LOGGER.text(new_version)\n
check_licenses(ctx)\n
Check licenses for compatibility with licensecheck.
licensecheck
@task()\ndef check_licenses(ctx: Context) -> None:\n \"\"\"Check licenses for compatibility with `licensecheck`.\"\"\"\n res = run(ctx, 'which licensecheck', warn=True, hide=True)\n if not res or res.exited == 1:\n LOGGER.warning('`licensecheck` not found. installing with pipx')\n run(ctx, 'pipx install licensecheck')\n run(ctx, 'licensecheck')\n
install_extras(ctx)\n
Run poetry install with all extras.
@task(pre=[lock])\ndef install_extras(ctx: Context) -> None:\n \"\"\"Run poetry install with all extras.\"\"\"\n poetry_config = file_helpers.read_pyproject()['tool']['poetry']\n extras = (poetry_config.get('extras') or {}).keys()\n run(ctx, ' '.join(['poetry install --sync', *[f'--extras={ex}' for ex in extras]]))\n
lock(ctx)\n
Ensure poetry.lock is up-to-date.
@task()\ndef lock(ctx: Context) -> None:\n \"\"\"Ensure poetry.lock is up-to-date.\"\"\"\n if can_skip.can_skip(prerequisites=[PROJECT_TOML], targets=[LOCK]):\n return # Exit early\n\n run(ctx, 'poetry lock --no-update')\n
publish(ctx, *, to_test_pypi=False)\n
Build the distributed format(s) and publish.
@task(\n help={\n 'to_test_pypi': 'Publish to the TestPyPi repository',\n },\n)\ndef publish(ctx: Context, *, to_test_pypi: bool = False) -> None:\n \"\"\"Build the distributed format(s) and publish.\"\"\"\n run(ctx, f'{python_dir()}/nox --error-on-missing-interpreters --session build_dist build_check')\n\n cmd = 'poetry publish'\n if to_test_pypi:\n cmd += ' --repository testpypi'\n run(ctx, cmd)\n
Stale Packages CLI.
check_for_stale_packages(ctx, *, stale_months=48)\n
Identify stale dependencies.
@task(\n default=True,\n help={\n 'stale_months': 'Cutoff in months for when a package may be stale enough to be a risk',\n },\n)\ndef check_for_stale_packages(ctx: Context, *, stale_months: int = 48) -> None: # noqa: ARG001\n \"\"\"Identify stale dependencies.\"\"\"\n cfsp(stale_months=stale_months)\n
Code Tag Collector CLI.
collect_code_tags(ctx, base_dir='.', doc_sub_dir='', filename=None, tag_order='', regex='', ignore_patterns='')\n
Create a CODE_TAG_SUMMARY.md with a table for TODO- and FIXME-style code comments.
CODE_TAG_SUMMARY.md
@task(\n default=True,\n help={\n 'base_dir': 'Working Directory',\n 'doc_sub_dir': 'Subdirectory for output of the code tag summary file',\n 'filename': 'Code Tag Summary Filename',\n 'tag_order': 'Ordered list of code tags to locate (Comma-separated)',\n 'regex': 'Custom Code Tag Regex. Must contain \"{tag}\"',\n 'ignore_patterns': 'Glob patterns to ignore files and directories when searching (Comma-separated)',\n },\n)\ndef collect_code_tags( # noqa: PLR0917\n ctx: Context,\n base_dir: str = '.',\n doc_sub_dir: str = '',\n filename: Optional[str] = None,\n tag_order: str = '',\n regex: str = '',\n ignore_patterns: str = '',\n) -> None:\n \"\"\"Create a `CODE_TAG_SUMMARY.md` with a table for TODO- and FIXME-style code comments.\"\"\"\n pth_base_dir = Path(base_dir).resolve()\n pth_docs = pth_base_dir / doc_sub_dir if doc_sub_dir else get_doc_subdir()\n if filename and '/' in filename:\n raise RuntimeError('Unexpected slash in filename. You should consider setting `--doc-sub-dir` instead')\n path_tag_summary = pth_docs / (filename or from_ctx(ctx, 'tags', 'filename'))\n patterns = (ignore_patterns or from_ctx(ctx, 'tags', 'ignore_patterns')).split(',')\n paths_source = find_project_files(pth_base_dir, ignore_patterns=[pattern for pattern in patterns if pattern])\n\n write_code_tag_file(\n path_tag_summary=path_tag_summary,\n paths_source=paths_source,\n base_dir=pth_base_dir,\n regex=regex,\n tags=tag_order,\n header='# Collected Code Tags',\n )\n
Test CLI.
check(_ctx)\n
Run pytest checks, such as identifying .
@task()\ndef check(_ctx: Context) -> None:\n \"\"\"Run pytest checks, such as identifying .\"\"\"\n if duplciates := check_duplicate_test_names.run(Path('tests')):\n raise RuntimeError(f'Duplicate test names found ({duplciates}). See above for details.') # noqa: EM102\n
coverage(ctx, *, min_cover=0, out_dir=None, view=False)\n
Generate useful coverage outputs after running pytest.
Creates coverage.json used in doc.build
coverage.json
doc.build
@task(\n help={\n 'min_cover': 'Fail if coverage less than threshold',\n 'out_dir': 'Optional path to coverage directory. Typically \".cover\" or \"releases/tests\"',\n 'view': 'If True, open the created files',\n },\n)\ndef coverage(ctx: Context, *, min_cover: int = 0, out_dir: Optional[str] = None, view: bool = False) -> None:\n \"\"\"Generate useful coverage outputs after running pytest.\n\n Creates `coverage.json` used in `doc.build`\n\n \"\"\"\n pkg_name = read_package_name()\n _inner_task(ctx, cli_args='', min_cover=min_cover,\n command=f'coverage run --branch --source={pkg_name} --module pytest')\n\n cov_dir = Path(out_dir or from_ctx(ctx, 'test', 'out_dir'))\n cov_dir.mkdir(exist_ok=True, parents=True)\n print() # noqa: T201\n for cli_args in (\n 'report --show-missing', # Write to STDOUT\n f'html --directory={cov_dir}', # Write to HTML\n 'json', # Create coverage.json file for \"_handle_coverage\"\n ):\n run(ctx, f'{python_dir()}/coverage {cli_args}')\n\n if view: # pragma: no cover\n open_in_browser(cov_dir / 'index.html')\n
pytest(ctx, *, keyword='', marker='', min_cover=0)\n
Run pytest with default arguments.
Additional arguments can be set in the environment variable \u2018PYTEST_ADDOPTS\u2019
@task(\n default=True,\n help={\n 'min_cover': 'Fail if coverage less than threshold',\n **KM_HELP,\n },\n)\ndef pytest(ctx: Context, *, keyword: str = '', marker: str = '', min_cover: int = 0) -> None:\n \"\"\"Run pytest with default arguments.\n\n Additional arguments can be set in the environment variable 'PYTEST_ADDOPTS'\n\n \"\"\"\n pkg_name = read_package_name()\n durations = '--durations=25 --durations-min=\"0.1\"'\n _inner_task(ctx,\n cli_args=f' --cov={pkg_name} --cov-branch --cov-report=term-missing {durations}',\n keyword=keyword, marker=marker, min_cover=min_cover)\n
step(ctx, *, keyword='', marker='')\n
Run pytest optimized to stop on first error.
@task(help=KM_HELP)\ndef step(ctx: Context, *, keyword: str = '', marker: str = '') -> None:\n \"\"\"Run pytest optimized to stop on first error.\"\"\"\n _inner_task(ctx, cli_args=_STEPWISE_ARGS, keyword=keyword, marker=marker)\n
watch(ctx, *, keyword='', marker='')\n
Run pytest with polling and optimized to stop on first error.
@task(help=KM_HELP)\ndef watch(ctx: Context, *, keyword: str = '', marker: str = '') -> None:\n \"\"\"Run pytest with polling and optimized to stop on first error.\"\"\"\n _inner_task(ctx, cli_args=_STEPWISE_ARGS, keyword=keyword, marker=marker, command='ptw . --now')\n
Types CLI.
basedpyright(ctx)\n
Run basedpyright.
@task()\ndef basedpyright(ctx: Context) -> None:\n \"\"\"Run basedpyright.\"\"\"\n _inner_task(ctx, command=f'{python_dir()}/basedpyright')\n
mypy(ctx)\n
Run mypy.
@task()\ndef mypy(ctx: Context) -> None:\n \"\"\"Run mypy.\"\"\"\n _inner_task(ctx, command=f'{python_dir()}/mypy')\n
pyright(ctx)\n
Run pyright.
@task()\ndef pyright(ctx: Context) -> None:\n \"\"\"Run pyright.\"\"\"\n check_installed(ctx, executable='pyright', message=PYRIGHT_MESSAGE)\n _inner_task(ctx, command='pyright')\n