Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ per-file-ignores =
# additionally test docstrings don't need param lists (DAR, DCO020):
tests/**.py: DAR, DCO020, S101, S105, S108, S404, S603, WPS202, WPS210, WPS430, WPS436, WPS441, WPS442, WPS450

# The following ignores have been researched and should be considered permanent:
# WPS202: two-plugin module (secret store + SSH) with Protocol types, TypedDicts,
# and multiple helpers per plugin; restructuring would harm readability.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessarily. There's no reason why a single module can't be turned into a folder with some separation of common helpers and entry points, and maybe types.

src/awx_plugins/credentials/akeyless.py: WPS202

# The following ignores must be fixed and the entries removed from this config:
src/awx_plugins/credentials/aim.py: ANN003, ANN201, B950, CCR001, D100, D103, LN001, Q003, WPS210, WPS221, WPS223, WPS231, WPS336, WPS432
src/awx_plugins/credentials/aws_secretsmanager.py: ANN003, ANN201, D100, D103, WPS111, WPS210, WPS329, WPS529
Expand Down
24 changes: 24 additions & 0 deletions .mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ warn_unused_ignores = true
# crashes with some decorators like `@functools.cache`:
disallow_any_expr = false

[mypy-awx_plugins.credentials.akeyless]
# The akeyless SDK does not ship type stubs. These suppressions are needed
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of them? Why?

# until the SDK gains proper typing support:
disallow_any_expr = false
disallow_any_unimported = false
disallow_any_explicit = false
disallow_untyped_calls = false
warn_return_any = false

[mypy-awx_plugins.credentials.aws_secretsmanager]
# crashes with some decorators like `@functools.cache`:
disallow_any_expr = false
Expand Down Expand Up @@ -106,8 +115,23 @@ disallow_any_expr = false
# crashes with some decorators like `@tox.plugin.impl`:
disallow_any_expr = false

[mypy-akeyless]
# The akeyless SDK does not ship type stubs:
ignore_missing_imports = true

[mypy-akeyless.*]
# The akeyless SDK does not ship type stubs:
ignore_missing_imports = true

Comment on lines +118 to +125
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[mypy-tests.*]
# crashes with some decorators like `@pytest.mark.parametrize`:
disallow_any_expr = false
# fails on `@hypothesis.given()`:
disallow_any_decorated = false
# fixture return types like `Callable[..., object]` use `...` as Any:
disallow_any_explicit = false

[mypy-tests.akeyless_test]
# Mock API objects are typed as `object`; test kwargs use generic dict
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather have these ignores inline

# rather than the specific TypedDicts the plugin functions expect:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't they be casted to whatever type necessary? You've done this in the runtime module already. No need to have multiple approaches for the same thing..

disable_error_code = attr-defined, arg-type
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ repos:
- pytest-mock # needed by pylint-pytest since it picks up pytest's args
- pytest-subtests # needed by pylint-pytest since it picks up pytest's args
- pytest-xdist # needed by pylint-pytest since it picks up pytest's args
- akeyless >= 5.0.8 # needed by credentials.akeyless and its tests
- python-dsv-sdk # needed by credentials.dsv, credentials.thycotic_dsv
- PyYAML # needed by credentials.injectors, inventory.plugins
- Sphinx # needed by the Sphinx extension stub
Expand Down
6 changes: 5 additions & 1 deletion .pylintrc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,11 @@ py-version = "3.11"
# source root is an absolute path or a path relative to the current working
# directory used to determine a package namespace for modules located under the
# source root.
# source-roots =
# Setting src/ prevents pylint from adding individual credential module
# directories to sys.path, which would cause false "module imports itself"
# warnings and circular-import errors for files with the same name as their
# third-party dependencies (e.g. akeyless.py vs the akeyless SDK):
source-roots = ["src"]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already achieved by the init-hook on line 64. I've been meaning to migrate to source-roots but didn't get to it. It's literally among the experiments in my Git checkout right now:

diff --git a/.pylintrc.toml b/.pylintrc.toml
index 3ffdc0e837..39b177a8d8 100644
--- a/.pylintrc.toml
+++ b/.pylintrc.toml
@@ -61,18 +61,19 @@ ignore-patterns = ["^\\.#"]
 # This patch injects the project directory into the import path so that the
 # local `pytest` plugin can be imported when `pylint-pytest` invokes it when
 # exploring the fixtures available:
-init-hook = """
-import os, pathlib, sys
-repo_root_path = pathlib.Path.cwd()
-src_path = repo_root_path / 'src'
-sys.path[:0] = [str(src_path if src_path.exists() else repo_root_path)]
-os.environ['PYTHONPATH'] = sys.path[0]
-"""
+# init-hook = """
+# import os, pathlib, sys
+# repo_root_path = pathlib.Path.cwd()
+# src_path = repo_root_path / 'src'
+# sys.path[:0] = [str(src_path if src_path.exists() else repo_root_path)]
+# os.environ['PYTHONPATH'] = sys.path[0]
+# """
 
 # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
 # number of processors available to use, and will cap the count on Windows to
 # avoid hangs.
-jobs = 0
+# jobs = 0
+jobs = 1
 
 # Control the amount of potential inferred values when inferring a single object.
 # This can help the performance when dealing with large functions or complex,
@@ -107,6 +108,9 @@ py-version = "3.11"
 # directory used to determine a package namespace for modules located under the
 # source root.
 # source-roots =
+source-roots = [
+  "src/",
+]
 
 # Allow loading of arbitrary C extensions. Extensions are imported into the
 # active Python interpreter and may run arbitrary code.
@@ -435,7 +439,7 @@ disable = [
   "disallowed-name",
   "duplicate-code",
   "fixme",
-  "import-outside-toplevel",
+  # "import-outside-toplevel",
   "invalid-name",
   "line-too-long",
   "missing-class-docstring",
@@ -444,11 +448,11 @@ disable = [
   "missing-timeout",
   "no-else-return",
   "no-member",
-  "no-name-in-module",  # false-positive: https://github.com/pylint-dev/pylint/issues/10147#issuecomment-3946199493
+  # "no-name-in-module",  # false-positive: https://github.com/pylint-dev/pylint/issues/10147#issuecomment-3946199493
   "no-self-use",
   "pointless-string-statement",
   "raise-missing-from",
-  "relative-beyond-top-level",
+  # "relative-beyond-top-level",
   "singleton-comparison",
   "too-few-public-methods",
   "too-many-branches",

But regardless, this seems like something that doesn't belong in this PR but could be submitted separately as a linting config / infra improvement rather than related to a plugin being presented. Would you like to send another PR with just this migration? (the init-hook-to-source-roots parts of the snippet)


# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
Expand Down
3 changes: 3 additions & 0 deletions .ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ testing = [
"S101", # Allow use of `assert` in test files
"S105", # hardcoded-password-string
"S106", # hardcoded-password-func-arg
"S107", # hardcoded-password-func-default: test helper factories use
# credential-like param names (token, secret_data) as configurable
# defaults — these are never real credentials
"S108", # tmp dirs
"S404", # Allow importing 'subprocess' module to testing call external tools needed by these hooks
"S603", # subprocess calls
Expand Down
6 changes: 6 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,12 @@
'awx_plugins.interfaces._temporary_private_credential_api.Credential',
),
('py:class', 'EnvVarsType'),
# Akeyless SDK types: auto-generated from OpenAPI
# without type annotations or .pyi stubs
('py:class', 'akeyless.models.auth.Auth'),
('py:class', 'akeyless.models.describe_item.DescribeItem'),
('py:class', 'akeyless.models.get_secret_value.GetSecretValue'),
('py:class', 'akeyless.models.get_ssh_certificate.GetSSHCertificate'),
]


Expand Down
1 change: 1 addition & 0 deletions docs/spelling_wordlist.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Akeyless
Ansible
Approle
async
Expand Down
10 changes: 10 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ centrify_vault_kv = "awx_plugins.credentials.centrify_vault:centrify_plugin"
thycotic_dsv = "awx_plugins.credentials.dsv:dsv_plugin"
thycotic_tss = "awx_plugins.credentials.tss:tss_plugin"
aws_secretsmanager_credential = "awx_plugins.credentials.aws_secretsmanager:aws_secretmanager_plugin"
akeyless = "awx_plugins.credentials.akeyless:akeyless_plugin"
akeyless_ssh = "awx_plugins.credentials.akeyless:akeyless_ssh_plugin"
github_app_lookup = "awx_plugins.credentials.github_app:github_app_lookup"

[project.entry-points."awx_plugins.managed_credentials"] # new entry points group name
Expand Down Expand Up @@ -189,6 +191,14 @@ credentials-aws-secretsmanager-credential = [
"awx_plugins.interfaces",
"boto3",
]
credentials-akeyless = [
"awx_plugins.interfaces",
"akeyless >= 5.0.8",
]
credentials-akeyless-ssh = [
"awx_plugins.interfaces",
"akeyless >= 5.0.8",
]
inventory-azure-rm = [
"awx_plugins.interfaces",
"PyYAML",
Expand Down
4 changes: 4 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
[pytest]
# Add src/ to sys.path so tests can import the package even in environments
# where `pip install -e .` has not been run (e.g. the pre-commit pylint venv):
pythonpath = src
Copy link
Copy Markdown
Member

@webknjaz webknjaz Mar 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely a wrong infra change. Things aren't supposed to be imported from the tree, only from installed envs. Additionally, pytest isn't supposed to be run directly.


addopts =
# `pytest-xdist`:
--numprocesses=auto
Expand Down
Loading
Loading