-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Add --max-depth option to control diagram complexity #10077
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #10077 +/- ##
==========================================
+ Coverage 95.80% 95.84% +0.03%
==========================================
Files 174 175 +1
Lines 18976 19056 +80
==========================================
+ Hits 18180 18264 +84
+ Misses 796 792 -4
π New features to boost your workflow:
|
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Quick update: I've completed the implementation and added tests for the --max-depth option. I believe the feature is working as intended and is ready for review. Iβm currently fighting some CI issues, but they seem unrelated to my changes. Any help or advice would be much appreciated! |
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM ! Don't worry about the 3.13 CI fail, this is on main too we can rebase once it's fixed. I'll also wait for a review from Andrea before merging.
Thanks! Appreciate your review and the clarification about the CI issue. I`ll keep an eye on any updates and am happy to rebase if needed. |
This comment has been minimized.
This comment has been minimized.
Thank you for rebasing, the mypy error is coming from main the other errors seems genuine. (Spelling is checked only in ci not in local pre-commit) |
Thanks for pointing out the mypy error, I`ll keep an eye on it if it gets fixed in main. How about disabling spelling check for these test cases because "-" is not allowed in Python naming anyway? |
This comment has been minimized.
This comment has been minimized.
It seems that the spelling checks still fail, because it checks for words and ignores the quotes. How about adding these testnames to [SPELLING]
# List of comma separated words that should not be checked.
spelling-ignore-words=subpkg,MyClass |
This comment has been minimized.
This comment has been minimized.
When working on test coverage I noticed tests/data/init.py is empty, which impacts pyreverse's ability to detect complete package relationships. This effects validation of the --max-depth functionality since module relationships aren't fully established and the resulting diagrams do not represent the whole structure. @Pierre-Sassoulas do you maybe remember if this was an intentional design choice, or did this just grow over time? Note: Addressing this would require modifying all affected test files to properly reflect package relationships. Given the scope, I think handling such changes in a separate PR would be better, if we decide to do this! |
I'm not sure. Pylint had a lot of issues with namespace package and missing |
Thanks for checking! You're right that it's about an empty (not missing) init.py. The difference is how many relationships pyreverse detects:
I think this effects depth filtering coverage since we're not testing package-level relationship filtering. Although I want to add, that I am not a testing expert. Here is what I added to init.py: from .clientmodule_test import Ancestor, Specialization
from .suppliermodule_test import Interface, DoNothing, DoNothing2
from .nullable_pattern import NullablePatterns
from .property_pattern import PropertyPatterns |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Thanks for your earlier guidance, Pierre! I hope this is PR is not getting to large. I now rebased to fix the mypy error and addressed the spelling issues. Also, I made some mistakes when setting up the tests, which resulted in pyreverse generating wrong diagrams. Now I use a proper test setup with fixtures and test multiple depths, to improve test coverage. The issue with pyreverse not properly detecting all relationships when init.py is empty still remains. I believe this is why coverage for package-level relationships is still incomplete. In my understanding, an empty init.py file limits the ability to fully establish package-level relationships, as it doesnβt define imports or explicitly link modules in the package. This might explain why pyreverse struggles with package-level filtering in such cases. However, Iβm not entirely sure if this is the root cause or if thereβs another factor at play. Iβm happy to explore this further and improve the tests in a follow-up PR if needed. Let me know what you thinkπ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the delay. Changes seems sensible, I have a possible refactor to suggest. (I'd like a review from Andras if possible though, no pressure @DudeNr33 ;) )
properties=self.get_class_properties(obj), | ||
) | ||
# inheritance links | ||
for rel in diagram.get_relationships("specialization"): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for rel in diagram.get_relationships("specialization"): | |
for rel in diagram.get_relationships("specialization", depth=self.max_depth): |
Wonder if it's possible of filtering on depth directly in get_relationships
instead. Seems like it could reduce duplication of .should_show_node
checks when looping on it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion! I would agree, that handling everything regarding filtering in one place would benefit maintainability. However I think we still need node filtering when emitting nodes, to determine which ones to show in the final diagram. Not sure about splitting this up into 2 files either. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I feel like we could simplify this, by introducing get_nodes()
alongside get_relationships()
. Then the writer would look something like this.
def write_packages(self, diagram: PackageDiagram) -> None:
for module in diagram.get_nodes(depth=self.max_depth):
# Emit node logic
for rel in diagram.get_relationships(depth=self.max_depth):
# Emit edge logic
That way all the filtering logic would be handled consistently inside the diagram classes and the printer would only need to focus on rendering the final components. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is a big change, I am not sure how to approach this. Are you ok with changing it in this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, let's open another PR for the refactor required to add the depth argument.
No worries, Currently I am also very packed. Will try to take a look at it later this week :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the delay, I am not very active on GitHub at the moment.
Thank you for the PR - this is actually a feature I saw in a similar tool and always thought "would be really helpful to have this in pyreverse"!
Regarding the changes (and following the discussion you already had with Pierre), I feel like the logic is better suited in diadefslib.py
, and could be handled similarly to --show_associated
.
But like Pierre I think handling this in a follow-up PR makes sense, so that this one does not get too big.
Besides that I only have on remark regarding the option group. π
Thanks, appreciate your review. Both of your suggestions make sense - especially the architectural one about moving depth filtering to diadefslib.py alongside other filters like --show_associated. I've moved the --max-depth option to the Filtering and Scope group to better align with similar options. I am happy to do a refactor in a follow up PR :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Now I guess only the failing codecov check in the pipeline prevents this from being merged.
Do you need help with that?
Thanks for checking! As I said here:
I believe this is due to empty |
Hey @Pierre-Sassoulas , I believe I am not able to resolve codecov, without changing the structure of the testfiles. How about merging this and doing the proposed refactor in a follow-up PR? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's do that !
This PR contains the following updates: | Package | Change | Age | Confidence | |---|---|---|---| | [pylint](https://github.com/pylint-dev/pylint) ([changelog](https://pylint.readthedocs.io/en/latest/whatsnew/3/)) | `<4.0.0,>=3.3.3` -> `<4.1.0,>=4.0.0` | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes <details> <summary>pylint-dev/pylint (pylint)</summary> ### [`v4.0.0`](https://github.com/pylint-dev/pylint/releases/tag/v4.0.0) [Compare Source](pylint-dev/pylint@v3.3.9...v4.0.0) - Pylint now supports Python 3.14. - Pylint's inference engine (`astroid`) is now much more precise, understanding implicit booleanness and ternary expressions. (Thanks [@​zenlyj](https://github.com/zenlyj)!) Consider this example: ```python class Result: errors: dict | None = None result = Result() if result.errors: result.errors[field_key] ### inference engine understands result.errors cannot be None ### pylint no longer raises unsubscriptable-object ``` The required `astroid` version is now 4.0.0. See the [astroid changelog](https://pylint.readthedocs.io/projects/astroid/en/latest/changelog.html#what-s-new-in-astroid-4-0-0) for additional fixes, features, and performance improvements applicable to pylint. - Handling of `invalid-name` at the module level was patchy. Now, module-level constants that are reassigned are treated as variables and checked against `--variable-rgx` rather than `--const-rgx`. Module-level lists, sets, and objects can pass against either regex. Here, `LIMIT` is reassigned, so pylint only uses `--variable-rgx`: ```python LIMIT = 500 # [invalid-name] if sometimes: LIMIT = 1 # [invalid-name] ``` If this is undesired, refactor using *exclusive* assignment so that it is evident that this assignment happens only once: ```python if sometimes: LIMIT = 1 else: LIMIT = 500 # exclusive assignment: uses const regex, no warning ``` Lists, sets, and objects still pass against either `const-rgx` or `variable-rgx` even if reassigned, but are no longer completely skipped: ```python MY_LIST = [] my_list = [] My_List = [] # [invalid-name] ``` Remember to adjust the [regexes](https://pylint.readthedocs.io/en/latest/user_guide/messages/convention/invalid-name.html) and [allow lists](https://pylint.readthedocs.io/en/latest/user_guide/configuration/all-options.html#good-names) to your liking. ## Breaking Changes - `invalid-name` now distinguishes module-level constants that are assigned only once from those that are reassigned and now applies `--variable-rgx` to the latter. Values other than literals (lists, sets, objects) can pass against either the constant or variable regexes (e.g. "LOGGER" or "logger" but not "LoGgEr"). Remember that `--good-names` or `--good-names-rgxs` can be provided to explicitly allow good names. Closes [#​3585](pylint-dev/pylint#3585) - The unused `pylintrc` argument to `PyLinter.__init__()` is deprecated and will be removed. Refs [#​6052](pylint-dev/pylint#6052) - Commented out code blocks such as `# bar() # TODO: remove dead code` will no longer emit `fixme`. Refs [#​9255](pylint-dev/pylint#9255) - `pyreverse` `Run` was changed to no longer call `sys.exit()` in its `__init__`. You should now call `Run(args).run()` which will return the exit code instead. Having a class that always raised a `SystemExit` exception was considered a bug. Normal usage of pyreverse through the CLI will not be affected by this change. Refs [#​9689](pylint-dev/pylint#9689) - The `suggestion-mode` option was removed, as pylint now always emits user-friendly hints instead of false-positive error messages. You should remove it from your conf if it's defined. Refs [#​9962](pylint-dev/pylint#9962) - The `async.py` checker module has been renamed to `async_checker.py` since `async` is a Python keyword and cannot be imported directly. This allows for better testing and extensibility of the async checker functionality. Refs [#​10071](pylint-dev/pylint#10071) - The message-id of `continue-in-finally` was changed from `E0116` to `W0136`. The warning is now emitted for every Python version since it will raise a syntax warning in Python 3.14. See [PEP 765 - Disallow return/break/continue that exit a finally block](https://peps.python.org/pep-0765/). Refs [#​10480](pylint-dev/pylint#10480) - Removed support for `nmp.NaN` alias for `numpy.NaN` being recognized in ':ref:`nan-comparison`'. Use `np` or `numpy` instead. Refs [#​10583](pylint-dev/pylint#10583) - Version requirement for `isort` has been bumped to >=5.0.0. The internal compatibility for older `isort` versions exposed via `pylint.utils.IsortDriver` has been removed. Refs [#​10637](pylint-dev/pylint#10637) ## New Features - `comparison-of-constants` now uses the unicode from the ast instead of reformatting from the node's values preventing some bad formatting due to `utf-8` limitation. The message now uses `"` instead of `'` to better work with what the python ast returns. Refs [#​8736](pylint-dev/pylint#8736) - Enhanced pyreverse to properly distinguish between UML relationship types (association, aggregation, composition) based on object ownership semantics. Type annotations without assignment are now treated as associations, parameter assignments as aggregations, and object instantiation as compositions. Closes [#​9045](pylint-dev/pylint#9045) Closes [#​9267](pylint-dev/pylint#9267) - The `fixme` check can now search through docstrings as well as comments, by using `check-fixme-in-docstring = true` in the `[tool.pylint.miscellaneous]` section. Closes [#​9255](pylint-dev/pylint#9255) - The `use-implicit-booleaness-not-x` checks now distinguish between comparisons used in boolean contexts and those that are not, enabling them to provide more accurate refactoring suggestions. Closes [#​9353](pylint-dev/pylint#9353) - The verbose option now outputs the filenames of the files that have been checked. Previously, it only included the number of checked and skipped files. Closes [#​9357](pylint-dev/pylint#9357) - colorized reporter now colorizes messages/categories that have been configured as `fail-on` in red inverse. This makes it easier to quickly find the errors that are causing pylint CI job failures. Closes [#​9898](pylint-dev/pylint#9898) - Enhanced support for [@​property](https://github.com/property) decorator in pyreverse to correctly display return types of annotated properties when generating class diagrams. Closes [#​10057](pylint-dev/pylint#10057) - Add --max-depth option to pyreverse to control diagram complexity. A depth of 0 shows only top-level packages, 1 shows one level of subpackages, etc. This helps manage visualization of large codebases by limiting the depth of displayed packages and classes. Refs [#​10077](pylint-dev/pylint#10077) - Handle deferred evaluation of annotations in Python 3.14. Closes [#​10149](pylint-dev/pylint#10149) - Enhanced pyreverse to properly detect aggregations for comprehensions (list, dict, set, generator). Closes [#​10236](pylint-dev/pylint#10236) - `pyreverse`: add support for colorized output when using output format `mmd` (MermaidJS) and `html`. Closes [#​10242](pylint-dev/pylint#10242) - pypy 3.11 is now officially supported. Refs [#​10287](pylint-dev/pylint#10287) - Add support for Python 3.14. Refs [#​10467](pylint-dev/pylint#10467) - Add naming styles for `ParamSpec` and `TypeVarTuple` that align with the `TypeVar` style. Refs [#​10541](pylint-dev/pylint#10541) ## New Checks - Add `match-statements` checker and the following message: `bare-name-capture-pattern`. This will emit an error message when a name capture pattern is used in a match statement which would make the remaining patterns unreachable. This code is a SyntaxError at runtime. Closes [#​7128](pylint-dev/pylint#7128) - Add new check `async-context-manager-with-regular-with` to detect async context managers used with regular `with` statements instead of `async with`. Refs [#​10408](pylint-dev/pylint#10408) - Add `break-in-finally` warning. Using `break` inside the `finally` clause will raise a syntax warning in Python 3.14. See `PEP 765 - Disallow return/break/continue that exit a finally block <https://peps.python.org/pep-0765/>`\_. Refs [#​10480](pylint-dev/pylint#10480) - Add new checks for invalid uses of class patterns in :keyword:`match`. - :ref:`invalid-match-args-definition` is emitted if :py:data:`object.__match_args__` isn't a tuple of strings. - :ref:`too-many-positional-sub-patterns` if there are more positional sub-patterns than specified in :py:data:`object.__match_args__`. - :ref:`multiple-class-sub-patterns` if there are multiple sub-patterns for the same attribute. Refs [#​10559](pylint-dev/pylint#10559) - Add additional checks for suboptimal uses of class patterns in :keyword:`match`. - :ref:`match-class-bind-self` is emitted if a name is bound to `self` instead of using an `as` pattern. - :ref:`match-class-positional-attributes` is emitted if a class pattern has positional attributes when keywords could be used. Refs [#​10587](pylint-dev/pylint#10587) - Add a `consider-math-not-float` message. `float("nan")` and `float("inf")` are slower than their counterpart `math.inf` and `math.nan` by a factor of 4 (notwithstanding the initial import of math) and they are also not well typed when using mypy. This check also catches typos in float calls as a side effect. The :ref:`pylint.extensions.code_style` need to be activated for this check to work. Refs [#​10621](pylint-dev/pylint#10621) ## False Positives Fixed - Fix a false positive for `used-before-assignment` when a variable defined under an `if` and via a named expression (walrus operator) is used later when guarded under the same `if` test. Closes [#​10061](pylint-dev/pylint#10061) - Fix :ref:`no-name-in-module` for members of `concurrent.futures` with Python 3.14. Closes [#​10632](pylint-dev/pylint#10632) ## False Negatives Fixed - Fix false negative for `used-before-assignment` when a `TYPE_CHECKING` import is used as a type annotation prior to erroneous usage. Refs [#​8893](pylint-dev/pylint#8893) - Match cases are now counted as edges in the McCabe graph and will increase the complexity accordingly. Refs [#​9667](pylint-dev/pylint#9667) - Check module-level constants with type annotations for `invalid-name`. Remember to adjust `const-naming-style` or `const-rgx` to your liking. Closes [#​9770](pylint-dev/pylint#9770) - Fix false negative where function-redefined (E0102) was not reported for functions with a leading underscore. Closes [#​9894](pylint-dev/pylint#9894) - We now raise a `logging-too-few-args` for format string with no interpolation arguments at all (i.e. for something like `logging.debug("Awaiting process %s")` or `logging.debug("Awaiting process {pid}")`). Previously we did not raise for such case. Closes [#​9999](pylint-dev/pylint#9999) - Fix false negative for `used-before-assignment` when a function is defined inside a `TYPE_CHECKING` guard block and used later. Closes [#​10028](pylint-dev/pylint#10028) - Fix a false negative for `possibly-used-before-assignment` when a variable is conditionally defined and later assigned to a type-annotated variable. Closes [#​10421](pylint-dev/pylint#10421) - Fix false negative for `deprecated-module` when a `__import__` method is used instead of `import` sentence. Refs [#​10453](pylint-dev/pylint#10453) - Count match cases for `too-many-branches` check. Refs [#​10542](pylint-dev/pylint#10542) - Fix false-negative where :ref:`unused-import` was not reported for names referenced in a preceding `global` statement. Refs [#​10633](pylint-dev/pylint#10633) ## Other Bug Fixes - When displaying unicode with surrogates (or other potential `UnicodeEncodeError`), pylint will now display a '?' character (using `encode(encoding="utf-8", errors="replace")`) instead of crashing. The functional tests classes are also updated to handle this case. Closes [#​8736](pylint-dev/pylint#8736) - Fixed unidiomatic-typecheck only checking left-hand side. Closes [#​10217](pylint-dev/pylint#10217) - Fix a crash caused by malformed format strings when using `.format` with keyword arguments. Closes [#​10282](pylint-dev/pylint#10282) - Fix false positive `inconsistent-return-statements` when using `quit()` or `exit()` functions. Closes [#​10508](pylint-dev/pylint#10508) - Fix a crash in :ref:`nested-min-max` when using `builtins.min` or `builtins.max` instead of `min` or `max` directly. Closes [#​10626](pylint-dev/pylint#10626) - Fixed a crash in :ref:`unnecessary-dict-index-lookup` when the index of an enumerated list was deleted inside a for loop. Closes [#​10627](pylint-dev/pylint#10627) ## Other Changes - Remove support for launching pylint with Python 3.9. Code that supports Python 3.9 can still be linted with the `--py-version=3.9` setting. Refs [#​10405](pylint-dev/pylint#10405) ## Internal Changes - Modified test framework to allow for different test output for different Python versions. Refs [#​10382](pylint-dev/pylint#10382) </details> --- ### Configuration π **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). π¦ **Automerge**: Disabled by config. Please merge this manually once you are satisfied. β» **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. π **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNDYuMCIsInVwZGF0ZWRJblZlciI6IjQxLjE0Ni4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJ0eXBlL2RlcGVuZGVuY2llcyJdfQ==--> Reviewed-on: https://git.tainton.uk/repos/roboluke/pulls/384 Co-authored-by: renovate[bot] <[email protected]> Co-committed-by: renovate[bot] <[email protected]>
This PR contains the following updates: | Package | Change | Age | Confidence | |---|---|---|---| | [pylint](https://github.com/pylint-dev/pylint) ([changelog](https://pylint.readthedocs.io/en/latest/whatsnew/3/)) | `<4.0.0,>=3.3.3` -> `<4.1.0,>=4.0.0` | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes <details> <summary>pylint-dev/pylint (pylint)</summary> ### [`v4.0.0`](https://github.com/pylint-dev/pylint/releases/tag/v4.0.0) [Compare Source](pylint-dev/pylint@v3.3.9...v4.0.0) - Pylint now supports Python 3.14. - Pylint's inference engine (`astroid`) is now much more precise, understanding implicit booleanness and ternary expressions. (Thanks [@​zenlyj](https://github.com/zenlyj)!) Consider this example: ```python class Result: errors: dict | None = None result = Result() if result.errors: result.errors[field_key] ### inference engine understands result.errors cannot be None ### pylint no longer raises unsubscriptable-object ``` The required `astroid` version is now 4.0.0. See the [astroid changelog](https://pylint.readthedocs.io/projects/astroid/en/latest/changelog.html#what-s-new-in-astroid-4-0-0) for additional fixes, features, and performance improvements applicable to pylint. - Handling of `invalid-name` at the module level was patchy. Now, module-level constants that are reassigned are treated as variables and checked against `--variable-rgx` rather than `--const-rgx`. Module-level lists, sets, and objects can pass against either regex. Here, `LIMIT` is reassigned, so pylint only uses `--variable-rgx`: ```python LIMIT = 500 # [invalid-name] if sometimes: LIMIT = 1 # [invalid-name] ``` If this is undesired, refactor using *exclusive* assignment so that it is evident that this assignment happens only once: ```python if sometimes: LIMIT = 1 else: LIMIT = 500 # exclusive assignment: uses const regex, no warning ``` Lists, sets, and objects still pass against either `const-rgx` or `variable-rgx` even if reassigned, but are no longer completely skipped: ```python MY_LIST = [] my_list = [] My_List = [] # [invalid-name] ``` Remember to adjust the [regexes](https://pylint.readthedocs.io/en/latest/user_guide/messages/convention/invalid-name.html) and [allow lists](https://pylint.readthedocs.io/en/latest/user_guide/configuration/all-options.html#good-names) to your liking. ## Breaking Changes - `invalid-name` now distinguishes module-level constants that are assigned only once from those that are reassigned and now applies `--variable-rgx` to the latter. Values other than literals (lists, sets, objects) can pass against either the constant or variable regexes (e.g. "LOGGER" or "logger" but not "LoGgEr"). Remember that `--good-names` or `--good-names-rgxs` can be provided to explicitly allow good names. Closes [#​3585](pylint-dev/pylint#3585) - The unused `pylintrc` argument to `PyLinter.__init__()` is deprecated and will be removed. Refs [#​6052](pylint-dev/pylint#6052) - Commented out code blocks such as `# bar() # TODO: remove dead code` will no longer emit `fixme`. Refs [#​9255](pylint-dev/pylint#9255) - `pyreverse` `Run` was changed to no longer call `sys.exit()` in its `__init__`. You should now call `Run(args).run()` which will return the exit code instead. Having a class that always raised a `SystemExit` exception was considered a bug. Normal usage of pyreverse through the CLI will not be affected by this change. Refs [#​9689](pylint-dev/pylint#9689) - The `suggestion-mode` option was removed, as pylint now always emits user-friendly hints instead of false-positive error messages. You should remove it from your conf if it's defined. Refs [#​9962](pylint-dev/pylint#9962) - The `async.py` checker module has been renamed to `async_checker.py` since `async` is a Python keyword and cannot be imported directly. This allows for better testing and extensibility of the async checker functionality. Refs [#​10071](pylint-dev/pylint#10071) - The message-id of `continue-in-finally` was changed from `E0116` to `W0136`. The warning is now emitted for every Python version since it will raise a syntax warning in Python 3.14. See [PEP 765 - Disallow return/break/continue that exit a finally block](https://peps.python.org/pep-0765/). Refs [#​10480](pylint-dev/pylint#10480) - Removed support for `nmp.NaN` alias for `numpy.NaN` being recognized in ':ref:`nan-comparison`'. Use `np` or `numpy` instead. Refs [#​10583](pylint-dev/pylint#10583) - Version requirement for `isort` has been bumped to >=5.0.0. The internal compatibility for older `isort` versions exposed via `pylint.utils.IsortDriver` has been removed. Refs [#​10637](pylint-dev/pylint#10637) ## New Features - `comparison-of-constants` now uses the unicode from the ast instead of reformatting from the node's values preventing some bad formatting due to `utf-8` limitation. The message now uses `"` instead of `'` to better work with what the python ast returns. Refs [#​8736](pylint-dev/pylint#8736) - Enhanced pyreverse to properly distinguish between UML relationship types (association, aggregation, composition) based on object ownership semantics. Type annotations without assignment are now treated as associations, parameter assignments as aggregations, and object instantiation as compositions. Closes [#​9045](pylint-dev/pylint#9045) Closes [#​9267](pylint-dev/pylint#9267) - The `fixme` check can now search through docstrings as well as comments, by using `check-fixme-in-docstring = true` in the `[tool.pylint.miscellaneous]` section. Closes [#​9255](pylint-dev/pylint#9255) - The `use-implicit-booleaness-not-x` checks now distinguish between comparisons used in boolean contexts and those that are not, enabling them to provide more accurate refactoring suggestions. Closes [#​9353](pylint-dev/pylint#9353) - The verbose option now outputs the filenames of the files that have been checked. Previously, it only included the number of checked and skipped files. Closes [#​9357](pylint-dev/pylint#9357) - colorized reporter now colorizes messages/categories that have been configured as `fail-on` in red inverse. This makes it easier to quickly find the errors that are causing pylint CI job failures. Closes [#​9898](pylint-dev/pylint#9898) - Enhanced support for [@​property](https://github.com/property) decorator in pyreverse to correctly display return types of annotated properties when generating class diagrams. Closes [#​10057](pylint-dev/pylint#10057) - Add --max-depth option to pyreverse to control diagram complexity. A depth of 0 shows only top-level packages, 1 shows one level of subpackages, etc. This helps manage visualization of large codebases by limiting the depth of displayed packages and classes. Refs [#​10077](pylint-dev/pylint#10077) - Handle deferred evaluation of annotations in Python 3.14. Closes [#​10149](pylint-dev/pylint#10149) - Enhanced pyreverse to properly detect aggregations for comprehensions (list, dict, set, generator). Closes [#​10236](pylint-dev/pylint#10236) - `pyreverse`: add support for colorized output when using output format `mmd` (MermaidJS) and `html`. Closes [#​10242](pylint-dev/pylint#10242) - pypy 3.11 is now officially supported. Refs [#​10287](pylint-dev/pylint#10287) - Add support for Python 3.14. Refs [#​10467](pylint-dev/pylint#10467) - Add naming styles for `ParamSpec` and `TypeVarTuple` that align with the `TypeVar` style. Refs [#​10541](pylint-dev/pylint#10541) ## New Checks - Add `match-statements` checker and the following message: `bare-name-capture-pattern`. This will emit an error message when a name capture pattern is used in a match statement which would make the remaining patterns unreachable. This code is a SyntaxError at runtime. Closes [#​7128](pylint-dev/pylint#7128) - Add new check `async-context-manager-with-regular-with` to detect async context managers used with regular `with` statements instead of `async with`. Refs [#​10408](pylint-dev/pylint#10408) - Add `break-in-finally` warning. Using `break` inside the `finally` clause will raise a syntax warning in Python 3.14. See `PEP 765 - Disallow return/break/continue that exit a finally block <https://peps.python.org/pep-0765/>`\_. Refs [#​10480](pylint-dev/pylint#10480) - Add new checks for invalid uses of class patterns in :keyword:`match`. - :ref:`invalid-match-args-definition` is emitted if :py:data:`object.__match_args__` isn't a tuple of strings. - :ref:`too-many-positional-sub-patterns` if there are more positional sub-patterns than specified in :py:data:`object.__match_args__`. - :ref:`multiple-class-sub-patterns` if there are multiple sub-patterns for the same attribute. Refs [#​10559](pylint-dev/pylint#10559) - Add additional checks for suboptimal uses of class patterns in :keyword:`match`. - :ref:`match-class-bind-self` is emitted if a name is bound to `self` instead of using an `as` pattern. - :ref:`match-class-positional-attributes` is emitted if a class pattern has positional attributes when keywords could be used. Refs [#​10587](pylint-dev/pylint#10587) - Add a `consider-math-not-float` message. `float("nan")` and `float("inf")` are slower than their counterpart `math.inf` and `math.nan` by a factor of 4 (notwithstanding the initial import of math) and they are also not well typed when using mypy. This check also catches typos in float calls as a side effect. The :ref:`pylint.extensions.code_style` need to be activated for this check to work. Refs [#​10621](pylint-dev/pylint#10621) ## False Positives Fixed - Fix a false positive for `used-before-assignment` when a variable defined under an `if` and via a named expression (walrus operator) is used later when guarded under the same `if` test. Closes [#​10061](pylint-dev/pylint#10061) - Fix :ref:`no-name-in-module` for members of `concurrent.futures` with Python 3.14. Closes [#​10632](pylint-dev/pylint#10632) ## False Negatives Fixed - Fix false negative for `used-before-assignment` when a `TYPE_CHECKING` import is used as a type annotation prior to erroneous usage. Refs [#​8893](pylint-dev/pylint#8893) - Match cases are now counted as edges in the McCabe graph and will increase the complexity accordingly. Refs [#​9667](pylint-dev/pylint#9667) - Check module-level constants with type annotations for `invalid-name`. Remember to adjust `const-naming-style` or `const-rgx` to your liking. Closes [#​9770](pylint-dev/pylint#9770) - Fix false negative where function-redefined (E0102) was not reported for functions with a leading underscore. Closes [#​9894](pylint-dev/pylint#9894) - We now raise a `logging-too-few-args` for format string with no interpolation arguments at all (i.e. for something like `logging.debug("Awaiting process %s")` or `logging.debug("Awaiting process {pid}")`). Previously we did not raise for such case. Closes [#​9999](pylint-dev/pylint#9999) - Fix false negative for `used-before-assignment` when a function is defined inside a `TYPE_CHECKING` guard block and used later. Closes [#​10028](pylint-dev/pylint#10028) - Fix a false negative for `possibly-used-before-assignment` when a variable is conditionally defined and later assigned to a type-annotated variable. Closes [#​10421](pylint-dev/pylint#10421) - Fix false negative for `deprecated-module` when a `__import__` method is used instead of `import` sentence. Refs [#​10453](pylint-dev/pylint#10453) - Count match cases for `too-many-branches` check. Refs [#​10542](pylint-dev/pylint#10542) - Fix false-negative where :ref:`unused-import` was not reported for names referenced in a preceding `global` statement. Refs [#​10633](pylint-dev/pylint#10633) ## Other Bug Fixes - When displaying unicode with surrogates (or other potential `UnicodeEncodeError`), pylint will now display a '?' character (using `encode(encoding="utf-8", errors="replace")`) instead of crashing. The functional tests classes are also updated to handle this case. Closes [#​8736](pylint-dev/pylint#8736) - Fixed unidiomatic-typecheck only checking left-hand side. Closes [#​10217](pylint-dev/pylint#10217) - Fix a crash caused by malformed format strings when using `.format` with keyword arguments. Closes [#​10282](pylint-dev/pylint#10282) - Fix false positive `inconsistent-return-statements` when using `quit()` or `exit()` functions. Closes [#​10508](pylint-dev/pylint#10508) - Fix a crash in :ref:`nested-min-max` when using `builtins.min` or `builtins.max` instead of `min` or `max` directly. Closes [#​10626](pylint-dev/pylint#10626) - Fixed a crash in :ref:`unnecessary-dict-index-lookup` when the index of an enumerated list was deleted inside a for loop. Closes [#​10627](pylint-dev/pylint#10627) ## Other Changes - Remove support for launching pylint with Python 3.9. Code that supports Python 3.9 can still be linted with the `--py-version=3.9` setting. Refs [#​10405](pylint-dev/pylint#10405) ## Internal Changes - Modified test framework to allow for different test output for different Python versions. Refs [#​10382](pylint-dev/pylint#10382) </details> --- ### Configuration π **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). π¦ **Automerge**: Disabled by config. Please merge this manually once you are satisfied. β» **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. π **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNDYuMCIsInVwZGF0ZWRJblZlciI6IjQxLjE0Ni4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJ0eXBlL2RlcGVuZGVuY2llcyJdfQ==--> Reviewed-on: https://git.tainton.uk/repos/epage/pulls/168 Co-authored-by: renovate[bot] <[email protected]> Co-committed-by: renovate[bot] <[email protected]>
Type of Changes
Description
This PR adds a new
--max-depth
option to pyreverse that allows basic controlling of the visualization depth of packages and classes in the generated diagrams. The depth is simply calculated by counting dots in qualified names, where depth 0 represents top-level packages/classes.For packages, the full path depth is considered. For classes, the depth is calculated based on their containing module to ensure meaningful class diagrams while respecting the depth limit.
Closes #9233