invoke-toolkit 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- invoke_toolkit-0.0.1/.pre-commit-config.yaml +43 -0
- invoke_toolkit-0.0.1/.secrets.baseline +85 -0
- invoke_toolkit-0.0.1/.vscode/launch.json +23 -0
- invoke_toolkit-0.0.1/.vscode/settings.json +25 -0
- invoke_toolkit-0.0.1/LICENSE.txt +9 -0
- invoke_toolkit-0.0.1/PKG-INFO +84 -0
- invoke_toolkit-0.0.1/README.md +55 -0
- invoke_toolkit-0.0.1/pyproject.toml +94 -0
- invoke_toolkit-0.0.1/src/invoke_toolkit/__about__.py +4 -0
- invoke_toolkit-0.0.1/src/invoke_toolkit/__init__.py +3 -0
- invoke_toolkit-0.0.1/src/invoke_toolkit/collections.py +100 -0
- invoke_toolkit-0.0.1/src/invoke_toolkit/tasks/__init__.py +0 -0
- invoke_toolkit-0.0.1/src/invoke_toolkit/tasks/completion.py +9 -0
- invoke_toolkit-0.0.1/src/invoke_toolkit/tasks/dist.py +3 -0
- invoke_toolkit-0.0.1/src/invoke_toolkit/tasks/plugin.py +16 -0
- invoke_toolkit-0.0.1/src/invoke_toolkit/utils/__init__.py +0 -0
- invoke_toolkit-0.0.1/src/invoke_toolkit/utils/inspection.py +41 -0
- invoke_toolkit-0.0.1/tasks.py +24 -0
- invoke_toolkit-0.0.1/tests/__init__.py +3 -0
- invoke_toolkit-0.0.1/tests/program/main.py +16 -0
- invoke_toolkit-0.0.1/tests/program/tasks/__init__.py +0 -0
- invoke_toolkit-0.0.1/tests/program/tasks/coll1.py +6 -0
- invoke_toolkit-0.0.1/tests/test_collection.py +39 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# This is an example configuration to enable detect-secrets in the pre-commit hook.
|
|
2
|
+
# Add this file to the root folder of your repository.
|
|
3
|
+
#
|
|
4
|
+
# Read pre-commit hook framework https://pre-commit.com/ for more details about the structure of config yaml file and how git pre-commit would invoke each hook.
|
|
5
|
+
#
|
|
6
|
+
# This line indicates we will use the hook from ibm/detect-secrets to run scan during committing phase.
|
|
7
|
+
repos:
|
|
8
|
+
- repo: https://github.com/ibm/detect-secrets
|
|
9
|
+
# If you desire to use a specific version of detect-secrets, you can replace `master` with other git revisions such as branch, tag or commit sha.
|
|
10
|
+
# You are encouraged to use static refs such as tags, instead of branch name
|
|
11
|
+
#
|
|
12
|
+
# Running "pre-commit autoupdate" automatically updates rev to latest tag
|
|
13
|
+
rev: 0.13.1+ibm.61.dss
|
|
14
|
+
hooks:
|
|
15
|
+
- id: detect-secrets # pragma: whitelist secret
|
|
16
|
+
# Add options for detect-secrets-hook binary. You can run `detect-secrets-hook --help` to list out all possible options.
|
|
17
|
+
# You may also run `pre-commit run detect-secrets` to preview the scan result.
|
|
18
|
+
# when "--baseline" without "--use-all-plugins", pre-commit scan with just plugins in baseline file
|
|
19
|
+
# when "--baseline" with "--use-all-plugins", pre-commit scan with all available plugins
|
|
20
|
+
# add "--fail-on-unaudited" to fail pre-commit for unaudited potential secrets
|
|
21
|
+
args: [--baseline, .secrets.baseline, --use-all-plugins]
|
|
22
|
+
exclude: 'src/fp_helper/_vendor/.*'
|
|
23
|
+
|
|
24
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
25
|
+
# Ruff version.
|
|
26
|
+
rev: v0.1.6
|
|
27
|
+
hooks:
|
|
28
|
+
# Run the linter.
|
|
29
|
+
- id: ruff
|
|
30
|
+
args: [ --fix ]
|
|
31
|
+
exclude: 'src/.*'
|
|
32
|
+
# Run the formatter.
|
|
33
|
+
- id: ruff-format
|
|
34
|
+
# exclude: 'src/.*'
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
- repo: https://github.com/codespell-project/codespell
|
|
38
|
+
rev: v2.2.4
|
|
39
|
+
hooks:
|
|
40
|
+
- id: codespell
|
|
41
|
+
types_or: [python, rst, markdown, cython, c]
|
|
42
|
+
additional_dependencies: [tomli]
|
|
43
|
+
# exclude: 'src/.*'
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"exclude": {
|
|
3
|
+
"files": "^.secrets.baseline$",
|
|
4
|
+
"lines": null
|
|
5
|
+
},
|
|
6
|
+
"generated_at": "2024-08-04T19:48:23Z",
|
|
7
|
+
"plugins_used": [
|
|
8
|
+
{
|
|
9
|
+
"name": "AWSKeyDetector"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"name": "ArtifactoryDetector"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "AzureStorageKeyDetector"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"base64_limit": 4.5,
|
|
19
|
+
"name": "Base64HighEntropyString"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"name": "BasicAuthDetector"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": "BoxDetector"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "CloudantDetector"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"ghe_instance": "github.ibm.com",
|
|
32
|
+
"name": "GheDetector"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "GitHubTokenDetector"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"hex_limit": 3,
|
|
39
|
+
"name": "HexHighEntropyString"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"name": "IbmCloudIamDetector"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "IbmCosHmacDetector"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "JwtTokenDetector"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"keyword_exclude": null,
|
|
52
|
+
"name": "KeywordDetector"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"name": "MailchimpDetector"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"name": "NpmDetector"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"name": "PrivateKeyDetector"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"name": "SlackDetector"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"name": "SoftlayerDetector"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"name": "SquareOAuthDetector"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name": "StripeDetector"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"name": "TwilioKeyDetector"
|
|
77
|
+
}
|
|
78
|
+
],
|
|
79
|
+
"results": {},
|
|
80
|
+
"version": "0.13.1+ibm.61.dss",
|
|
81
|
+
"word_list": {
|
|
82
|
+
"file": null,
|
|
83
|
+
"hash": null
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Use IntelliSense to learn about possible attributes.
|
|
3
|
+
// Hover to view descriptions of existing attributes.
|
|
4
|
+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
5
|
+
"version": "0.2.0",
|
|
6
|
+
"configurations": [
|
|
7
|
+
{
|
|
8
|
+
"name": "Python Debugger: Current File",
|
|
9
|
+
"type": "debugpy",
|
|
10
|
+
"request": "launch",
|
|
11
|
+
"program": "${file}",
|
|
12
|
+
"console": "integratedTerminal"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "Python Debugger: Test program (tests/program/main)",
|
|
16
|
+
"type": "debugpy",
|
|
17
|
+
"request": "launch",
|
|
18
|
+
"program": "${workspaceFolder}/tests/program/main.py",
|
|
19
|
+
"console": "integratedTerminal",
|
|
20
|
+
"cwd": "${workspaceFolder}/tests/program/"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"workbench.colorCustomizations": {
|
|
3
|
+
"activityBar.activeBackground": "#ba5c56",
|
|
4
|
+
"activityBar.background": "#ba5c56",
|
|
5
|
+
"activityBar.foreground": "#e7e7e7",
|
|
6
|
+
"activityBar.inactiveForeground": "#e7e7e799",
|
|
7
|
+
"activityBarBadge.background": "#77c87c",
|
|
8
|
+
"activityBarBadge.foreground": "#15202b",
|
|
9
|
+
"commandCenter.border": "#e7e7e799",
|
|
10
|
+
"sash.hoverBorder": "#ba5c56",
|
|
11
|
+
"statusBar.background": "#9d4640",
|
|
12
|
+
"statusBar.foreground": "#e7e7e7",
|
|
13
|
+
"statusBarItem.hoverBackground": "#ba5c56",
|
|
14
|
+
"statusBarItem.remoteBackground": "#9d4640",
|
|
15
|
+
"statusBarItem.remoteForeground": "#e7e7e7",
|
|
16
|
+
"titleBar.activeBackground": "#9d4640",
|
|
17
|
+
"titleBar.activeForeground": "#e7e7e7",
|
|
18
|
+
"titleBar.inactiveBackground": "#9d464099",
|
|
19
|
+
"titleBar.inactiveForeground": "#e7e7e799"
|
|
20
|
+
},
|
|
21
|
+
"peacock.color": "#9d4640",
|
|
22
|
+
"ruff.format.preview": true,
|
|
23
|
+
"ruff.lint.preview": true,
|
|
24
|
+
"ruff.showNotifications": "onWarning"
|
|
25
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-present Nahuel Defossé <nahuel.deofsse@gmail.com>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: invoke-toolkit
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A set of extended APIs for PyInvoke for composable scripts, plugins and richer output
|
|
5
|
+
Project-URL: Documentation, https://github.com/D3f0/invoke-toolkit#readme
|
|
6
|
+
Project-URL: Issues, https://github.com/D3f0/invoke-toolkit/issues
|
|
7
|
+
Project-URL: Source, https://github.com/D3f0/invoke-toolkit
|
|
8
|
+
Author-email: Nahuel Defossé <D3f0@users.noreply.github.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE.txt
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
14
|
+
Classifier: Operating System :: POSIX
|
|
15
|
+
Classifier: Operating System :: Unix
|
|
16
|
+
Classifier: Programming Language :: Python
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
19
|
+
Classifier: Topic :: Software Development
|
|
20
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Topic :: System :: Software Distribution
|
|
24
|
+
Classifier: Topic :: System :: Systems Administration
|
|
25
|
+
Requires-Python: >=3.8
|
|
26
|
+
Requires-Dist: invoke
|
|
27
|
+
Requires-Dist: rich
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# invoke-toolkit
|
|
31
|
+
|
|
32
|
+
A set of extended APIs for PyInvoke for composable scripts, plugins and richer output.
|
|
33
|
+
|
|
34
|
+
This extends the Collection from Invoke so it can create automatically collections.
|
|
35
|
+
|
|
36
|
+
[](https://pypi.org/project/invoke-toolkit)
|
|
37
|
+
[](https://pypi.org/project/invoke-toolkit)
|
|
38
|
+
|
|
39
|
+
-----
|
|
40
|
+
|
|
41
|
+
## Table of Contents
|
|
42
|
+
|
|
43
|
+
- [invoke-toolkit](#invoke-toolkit)
|
|
44
|
+
- [Table of Contents](#table-of-contents)
|
|
45
|
+
- [Features](#features)
|
|
46
|
+
- [Do I need this package](#do-i-need-this-package)
|
|
47
|
+
- [Installation](#installation)
|
|
48
|
+
- [Development](#development)
|
|
49
|
+
- [License](#license)
|
|
50
|
+
|
|
51
|
+
## Features
|
|
52
|
+
|
|
53
|
+
- Task discovery by namespace for extendable/composable CLIs
|
|
54
|
+
- Discovery to *plain old* tasks.py (or any other name)
|
|
55
|
+
- Integration with stand alone binaries for specific tasks
|
|
56
|
+
- **Future** Download binaries
|
|
57
|
+
|
|
58
|
+
## Do I need this package
|
|
59
|
+
|
|
60
|
+
If you have...
|
|
61
|
+
|
|
62
|
+
- Used `invoke` for a while and...
|
|
63
|
+
- Have a large `tasks.py` that needs to be modularized
|
|
64
|
+
- Have a lot of copy/pasted code in multiple `tasks.py` across multiple repos.
|
|
65
|
+
- Have exceeded the approach of a repository cloned as `~/tasks/` with more .py files that you want to manage.
|
|
66
|
+
- Or you want to combine various tasks defined in multiple directories
|
|
67
|
+
- You want to create a zipped (shiv) redistribute script for container environments
|
|
68
|
+
like Kubernetes based CI environments with only requiring the Python interpreter.
|
|
69
|
+
|
|
70
|
+
## Installation
|
|
71
|
+
|
|
72
|
+
```console
|
|
73
|
+
pip install invoke-toolkit
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Development
|
|
77
|
+
|
|
78
|
+
This project utilizes the `pre-commit` framework, make sure you run:
|
|
79
|
+
|
|
80
|
+
`pre-commit install`
|
|
81
|
+
|
|
82
|
+
## License
|
|
83
|
+
|
|
84
|
+
`invoke-toolkit` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# invoke-toolkit
|
|
2
|
+
|
|
3
|
+
A set of extended APIs for PyInvoke for composable scripts, plugins and richer output.
|
|
4
|
+
|
|
5
|
+
This extends the Collection from Invoke so it can create automatically collections.
|
|
6
|
+
|
|
7
|
+
[](https://pypi.org/project/invoke-toolkit)
|
|
8
|
+
[](https://pypi.org/project/invoke-toolkit)
|
|
9
|
+
|
|
10
|
+
-----
|
|
11
|
+
|
|
12
|
+
## Table of Contents
|
|
13
|
+
|
|
14
|
+
- [invoke-toolkit](#invoke-toolkit)
|
|
15
|
+
- [Table of Contents](#table-of-contents)
|
|
16
|
+
- [Features](#features)
|
|
17
|
+
- [Do I need this package](#do-i-need-this-package)
|
|
18
|
+
- [Installation](#installation)
|
|
19
|
+
- [Development](#development)
|
|
20
|
+
- [License](#license)
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- Task discovery by namespace for extendable/composable CLIs
|
|
25
|
+
- Discovery to *plain old* tasks.py (or any other name)
|
|
26
|
+
- Integration with stand alone binaries for specific tasks
|
|
27
|
+
- **Future** Download binaries
|
|
28
|
+
|
|
29
|
+
## Do I need this package
|
|
30
|
+
|
|
31
|
+
If you have...
|
|
32
|
+
|
|
33
|
+
- Used `invoke` for a while and...
|
|
34
|
+
- Have a large `tasks.py` that needs to be modularized
|
|
35
|
+
- Have a lot of copy/pasted code in multiple `tasks.py` across multiple repos.
|
|
36
|
+
- Have exceeded the approach of a repository cloned as `~/tasks/` with more .py files that you want to manage.
|
|
37
|
+
- Or you want to combine various tasks defined in multiple directories
|
|
38
|
+
- You want to create a zipped (shiv) redistribute script for container environments
|
|
39
|
+
like Kubernetes based CI environments with only requiring the Python interpreter.
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```console
|
|
44
|
+
pip install invoke-toolkit
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Development
|
|
48
|
+
|
|
49
|
+
This project utilizes the `pre-commit` framework, make sure you run:
|
|
50
|
+
|
|
51
|
+
`pre-commit install`
|
|
52
|
+
|
|
53
|
+
## License
|
|
54
|
+
|
|
55
|
+
`invoke-toolkit` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "invoke-toolkit"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "A set of extended APIs for PyInvoke for composable scripts, plugins and richer output"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
keywords = []
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "Nahuel Defossé", email = "D3f0@users.noreply.github.com" },
|
|
15
|
+
]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Programming Language :: Python",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: Implementation :: CPython",
|
|
22
|
+
"Operating System :: POSIX",
|
|
23
|
+
"Operating System :: Unix",
|
|
24
|
+
"Operating System :: MacOS :: MacOS X",
|
|
25
|
+
"Topic :: Software Development",
|
|
26
|
+
"Topic :: Software Development :: Build Tools",
|
|
27
|
+
"Topic :: Software Development :: Libraries",
|
|
28
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
29
|
+
"Topic :: System :: Software Distribution",
|
|
30
|
+
"Topic :: System :: Systems Administration",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
dependencies = [
|
|
34
|
+
"invoke",
|
|
35
|
+
"rich"
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Documentation = "https://github.com/D3f0/invoke-toolkit#readme"
|
|
40
|
+
Issues = "https://github.com/D3f0/invoke-toolkit/issues"
|
|
41
|
+
Source = "https://github.com/D3f0/invoke-toolkit"
|
|
42
|
+
|
|
43
|
+
[tool.hatch.version]
|
|
44
|
+
path = "src/invoke_toolkit/__about__.py"
|
|
45
|
+
|
|
46
|
+
[tool.hatch.envs.types]
|
|
47
|
+
extra-dependencies = [
|
|
48
|
+
"mypy>=1.0.0",
|
|
49
|
+
]
|
|
50
|
+
[tool.hatch.envs.types.scripts]
|
|
51
|
+
check = "mypy --install-types --non-interactive {args:src/invoke_toolkit tests}"
|
|
52
|
+
|
|
53
|
+
[tool.coverage.run]
|
|
54
|
+
source_pkgs = ["invoke_toolkit", "tests"]
|
|
55
|
+
branch = true
|
|
56
|
+
parallel = true
|
|
57
|
+
omit = [
|
|
58
|
+
"src/invoke_toolkit/__about__.py",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
[tool.coverage.paths]
|
|
62
|
+
invoke_toolkit = ["src/invoke_toolkit", "*/invoke-toolkit/src/invoke_toolkit"]
|
|
63
|
+
tests = ["tests", "*/invoke-toolkit/tests"]
|
|
64
|
+
|
|
65
|
+
[tool.coverage.report]
|
|
66
|
+
exclude_lines = [
|
|
67
|
+
"no cov",
|
|
68
|
+
"if __name__ == .__main__.:",
|
|
69
|
+
"if TYPE_CHECKING:",
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
[tool.hatch.envs.dev]
|
|
74
|
+
dependencies = [
|
|
75
|
+
# Invoke's original set of tasks
|
|
76
|
+
"pytest",
|
|
77
|
+
"pytest-virtualenv",
|
|
78
|
+
"coverage",
|
|
79
|
+
"pdbpp",
|
|
80
|
+
]
|
|
81
|
+
[tool.hatch.envs.dev.scripts]
|
|
82
|
+
run-coverage = "pytest --cov-config=pyproject.toml --cov=pkg --cov=tests"
|
|
83
|
+
run = "run-coverage --no-cov"
|
|
84
|
+
test = "pytest -v {}"
|
|
85
|
+
testdbg = "pytest -v --pdb {}"
|
|
86
|
+
|
|
87
|
+
[tool.hatch.envs.docs]
|
|
88
|
+
dependencies = [
|
|
89
|
+
"sphinx"
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
[tool.ruff.lint.per-file-ignores]
|
|
94
|
+
"*tasks*" = ["ARG001"]
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import pkgutil
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from types import ModuleType
|
|
6
|
+
from typing import Dict, Union
|
|
7
|
+
|
|
8
|
+
from invoke.collection import Collection as InvokeCollection
|
|
9
|
+
from invoke.util import debug
|
|
10
|
+
|
|
11
|
+
from invoke_toolkit.utils.inspection import get_calling_file_path
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CollectionError(Exception):
|
|
15
|
+
"""Base class for import discovery errors"""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CollectionNotImportedError(CollectionError):
|
|
19
|
+
...
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def import_submodules(package_name: str) -> Dict[str, ModuleType]:
|
|
23
|
+
"""
|
|
24
|
+
Import all submodules of a module from an imported module
|
|
25
|
+
|
|
26
|
+
:param package_name: Package name
|
|
27
|
+
:type package_name: str
|
|
28
|
+
:rtype: dict[types.ModuleType]
|
|
29
|
+
"""
|
|
30
|
+
debug("Importing submodules in %s", package_name)
|
|
31
|
+
try:
|
|
32
|
+
package = sys.modules[package_name]
|
|
33
|
+
except ImportError as import_error:
|
|
34
|
+
msg = f"Module {package_name} not imported"
|
|
35
|
+
raise CollectionNotImportedError(msg) from import_error
|
|
36
|
+
result = {}
|
|
37
|
+
for _loader, name, _is_pkg in pkgutil.walk_packages(package.__path__):
|
|
38
|
+
try:
|
|
39
|
+
result[name] = importlib.import_module(package_name + "." + name)
|
|
40
|
+
except (ImportError, SyntaxError) as error:
|
|
41
|
+
if not name.startswith("__"):
|
|
42
|
+
debug(f"Error loading {name}: {error}")
|
|
43
|
+
else:
|
|
44
|
+
debug(f"Error loading {name}: {error}")
|
|
45
|
+
|
|
46
|
+
return result
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class Collection(InvokeCollection):
|
|
50
|
+
"""
|
|
51
|
+
This Collection allows to load sub-collections from python package paths/namespaces
|
|
52
|
+
like `myscripts.tasks.*`
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def add_collections_from_namespace(self, namespace: str) -> bool:
|
|
56
|
+
"""Iterates over a namespace and imports the submodules"""
|
|
57
|
+
# Attempt simple import
|
|
58
|
+
ok = False
|
|
59
|
+
if namespace not in sys.modules:
|
|
60
|
+
debug(f"Attempting simple import of {namespace}")
|
|
61
|
+
try:
|
|
62
|
+
importlib.import_module(namespace)
|
|
63
|
+
ok = True
|
|
64
|
+
except ImportError:
|
|
65
|
+
debug(f"Failed to import {namespace}")
|
|
66
|
+
|
|
67
|
+
if not ok:
|
|
68
|
+
debug("Starting stack inspection to find module")
|
|
69
|
+
# Trying to import relative to caller's script
|
|
70
|
+
caller_path = get_calling_file_path(
|
|
71
|
+
# We're going to get the path of the file where this call
|
|
72
|
+
# was made
|
|
73
|
+
find_call_text=".add_collections_from_namespace("
|
|
74
|
+
)
|
|
75
|
+
debug(f"Adding {caller_path} in order to import {namespace}")
|
|
76
|
+
sys.path.append(caller_path)
|
|
77
|
+
# This should work even if there's no __init__ alongside the
|
|
78
|
+
# program main
|
|
79
|
+
importlib.import_module(namespace)
|
|
80
|
+
|
|
81
|
+
for name, module in import_submodules(namespace).items():
|
|
82
|
+
coll = Collection.from_module(module)
|
|
83
|
+
self.add_collection(coll=coll, name=name)
|
|
84
|
+
|
|
85
|
+
def load_plugins(self):
|
|
86
|
+
...
|
|
87
|
+
|
|
88
|
+
def load_directory(self, directory: Union[str, Path]) -> None:
|
|
89
|
+
"""Loads tasks from a folder"""
|
|
90
|
+
if isinstance(directory, str):
|
|
91
|
+
path = Path(directory)
|
|
92
|
+
elif not isinstance(directory, Path):
|
|
93
|
+
msg = f"The directory to load plugins is not a str/Path: {directory}:{type(directory)}"
|
|
94
|
+
raise TypeError(msg)
|
|
95
|
+
else:
|
|
96
|
+
path = directory
|
|
97
|
+
|
|
98
|
+
existing_paths = {pth for pth in sys.path if Path(pth).is_dir()}
|
|
99
|
+
if path not in existing_paths:
|
|
100
|
+
sys.path.append(str(path))
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from invoke import Context, task
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@task(default=True)
|
|
5
|
+
def list_(ctx: Context):
|
|
6
|
+
"""List plugins"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@task(default=True)
|
|
10
|
+
def add(ctx: Context, plugin_spec: str) -> None:
|
|
11
|
+
"""Add a plugin"""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@task(default=True)
|
|
15
|
+
def remove(ctx: Context, name: str) -> None:
|
|
16
|
+
"""Add a plugin"""
|
|
File without changes
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from invoke.util import debug
|
|
7
|
+
from rich import print
|
|
8
|
+
|
|
9
|
+
# def print_call_stack():
|
|
10
|
+
# """Prints the current call stack."""
|
|
11
|
+
# for frame_info in inspect.stack():
|
|
12
|
+
# filename, lineno, function, code_context, index = frame_info
|
|
13
|
+
# print(f"File: {filename}, Line: {lineno}, Function: {function}")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def print_rich_frames(frames: list):
|
|
17
|
+
print(frames, file=sys.stderr)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_calling_file_path(find_call_text: str) -> Optional[str]:
|
|
21
|
+
"""Returns the containing folder of the module where the find_call_text is located"""
|
|
22
|
+
|
|
23
|
+
# Get the frame object of the caller
|
|
24
|
+
start_offset = 2
|
|
25
|
+
stack = inspect.stack()
|
|
26
|
+
index_frame_dict = dict(enumerate(stack[start_offset:]))
|
|
27
|
+
# print_rich_frames(stack[:2])
|
|
28
|
+
# ...
|
|
29
|
+
# print_rich_frames(index_frame_dict)
|
|
30
|
+
frame = None
|
|
31
|
+
found = False
|
|
32
|
+
for i, frame in index_frame_dict.items():
|
|
33
|
+
if any(find_call_text in line for line in frame.code_context):
|
|
34
|
+
debug(f"Found '{find_call_text}' in {frame}, call offset {i+2}")
|
|
35
|
+
found = True
|
|
36
|
+
break
|
|
37
|
+
if not found:
|
|
38
|
+
return None
|
|
39
|
+
# Get the module object of the caller
|
|
40
|
+
containing_directory = str(Path(frame.filename).parent.parent)
|
|
41
|
+
return containing_directory
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from invoke import task, Context
|
|
2
|
+
import subprocess
|
|
3
|
+
|
|
4
|
+
REPO_ROOT = (
|
|
5
|
+
subprocess.check_output("git rev-parse --show-toplevel", shell="sh")
|
|
6
|
+
.strip()
|
|
7
|
+
.decode()
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@task()
|
|
12
|
+
def build(ctx: Context):
|
|
13
|
+
ctx.run("hatch build", pty=True)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@task()
|
|
17
|
+
def test(ctx: Context):
|
|
18
|
+
ctx.run("hatch run dev:pytest", pty=True)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@task()
|
|
22
|
+
def clean_dist(ctx: Context):
|
|
23
|
+
with ctx.cd(REPO_ROOT):
|
|
24
|
+
ctx.run("dist/*")
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from invoke.program import Program
|
|
2
|
+
|
|
3
|
+
from invoke_toolkit.collections import Collection
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestProgram:
|
|
7
|
+
...
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
ns = Collection()
|
|
11
|
+
ns.add_collections_from_namespace("program.tasks")
|
|
12
|
+
program = Program(name="test program", version="0.0.1", namespace=ns)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
if __name__ == "__main__":
|
|
16
|
+
program.run()
|
|
File without changes
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import pkgutil
|
|
3
|
+
import sys
|
|
4
|
+
import types
|
|
5
|
+
from unittest import mock
|
|
6
|
+
|
|
7
|
+
from invoke_toolkit.collections import Collection
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def my_function(package_path):
|
|
11
|
+
modules = []
|
|
12
|
+
for _, module_name, _ in pkgutil.walk_packages([package_path]):
|
|
13
|
+
modules.append(module_name)
|
|
14
|
+
return modules
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_collection_load_submodules(monkeypatch):
|
|
18
|
+
ns = Collection()
|
|
19
|
+
|
|
20
|
+
def mock_walk_packages(path):
|
|
21
|
+
# Simulate modules
|
|
22
|
+
module1 = types.ModuleType("not_to_import")
|
|
23
|
+
module2 = types.ModuleType("to_import.tasks.mod1")
|
|
24
|
+
module3 = types.ModuleType("to_import.tasks.mod2")
|
|
25
|
+
return [
|
|
26
|
+
("module1", "", module1),
|
|
27
|
+
("to_import.tasks.mod1", "", module2),
|
|
28
|
+
("to_import.tasks.mod2", "", module3),
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
def mock_import_module(name):
|
|
32
|
+
monkeypatch.setitem(name, types.ModuleType(name=name))
|
|
33
|
+
|
|
34
|
+
monkeypatch.setattr(importlib, "import_module", mock.MagicMock())
|
|
35
|
+
monkeypatch.setattr(pkgutil, "walk_packages", mock_walk_packages)
|
|
36
|
+
module_to_import = types.ModuleType(name="to_import")
|
|
37
|
+
monkeypatch.setitem(sys.modules, "to_import.tasks", module_to_import)
|
|
38
|
+
result = ns.add_collections_from_namespace("to_import.tasks")
|
|
39
|
+
breakpoint()
|