cosmic-ray 0.0.0__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.
- cosmic_ray-0.0.0/LICENCE.txt +19 -0
- cosmic_ray-0.0.0/MANIFEST.in +2 -0
- cosmic_ray-0.0.0/PKG-INFO +108 -0
- cosmic_ray-0.0.0/README.rst +39 -0
- cosmic_ray-0.0.0/pyproject.toml +93 -0
- cosmic_ray-0.0.0/setup.cfg +4 -0
- cosmic_ray-0.0.0/src/cosmic_ray/__init__.py +1 -0
- cosmic_ray-0.0.0/src/cosmic_ray/ast/__init__.py +109 -0
- cosmic_ray-0.0.0/src/cosmic_ray/ast/ast_query.py +115 -0
- cosmic_ray-0.0.0/src/cosmic_ray/cli.py +307 -0
- cosmic_ray-0.0.0/src/cosmic_ray/commands/__init__.py +10 -0
- cosmic_ray-0.0.0/src/cosmic_ray/commands/execute.py +53 -0
- cosmic_ray-0.0.0/src/cosmic_ray/commands/init.py +90 -0
- cosmic_ray-0.0.0/src/cosmic_ray/commands/new_config.py +54 -0
- cosmic_ray-0.0.0/src/cosmic_ray/config.py +115 -0
- cosmic_ray-0.0.0/src/cosmic_ray/distribution/__init__.py +1 -0
- cosmic_ray-0.0.0/src/cosmic_ray/distribution/distributor.py +15 -0
- cosmic_ray-0.0.0/src/cosmic_ray/distribution/http.py +175 -0
- cosmic_ray-0.0.0/src/cosmic_ray/distribution/local.py +32 -0
- cosmic_ray-0.0.0/src/cosmic_ray/exceptions.py +4 -0
- cosmic_ray-0.0.0/src/cosmic_ray/modules.py +38 -0
- cosmic_ray-0.0.0/src/cosmic_ray/mutating.py +193 -0
- cosmic_ray-0.0.0/src/cosmic_ray/operators/__init__.py +0 -0
- cosmic_ray-0.0.0/src/cosmic_ray/operators/binary_operator_replacement.py +90 -0
- cosmic_ray-0.0.0/src/cosmic_ray/operators/boolean_replacer.py +91 -0
- cosmic_ray-0.0.0/src/cosmic_ray/operators/break_continue.py +17 -0
- cosmic_ray-0.0.0/src/cosmic_ray/operators/comparison_operator_replacement.py +104 -0
- cosmic_ray-0.0.0/src/cosmic_ray/operators/exception_replacer.py +60 -0
- cosmic_ray-0.0.0/src/cosmic_ray/operators/keyword_replacer.py +27 -0
- cosmic_ray-0.0.0/src/cosmic_ray/operators/no_op.py +26 -0
- cosmic_ray-0.0.0/src/cosmic_ray/operators/number_replacer.py +43 -0
- cosmic_ray-0.0.0/src/cosmic_ray/operators/operator.py +90 -0
- cosmic_ray-0.0.0/src/cosmic_ray/operators/provider.py +61 -0
- cosmic_ray-0.0.0/src/cosmic_ray/operators/remove_decorator.py +26 -0
- cosmic_ray-0.0.0/src/cosmic_ray/operators/unary_operator_replacement.py +118 -0
- cosmic_ray-0.0.0/src/cosmic_ray/operators/util.py +23 -0
- cosmic_ray-0.0.0/src/cosmic_ray/operators/variable_inserter.py +80 -0
- cosmic_ray-0.0.0/src/cosmic_ray/operators/variable_replacer.py +101 -0
- cosmic_ray-0.0.0/src/cosmic_ray/operators/zero_iteration_for_loop.py +28 -0
- cosmic_ray-0.0.0/src/cosmic_ray/plugins.py +77 -0
- cosmic_ray-0.0.0/src/cosmic_ray/progress.py +129 -0
- cosmic_ray-0.0.0/src/cosmic_ray/testing.py +56 -0
- cosmic_ray-0.0.0/src/cosmic_ray/timing.py +43 -0
- cosmic_ray-0.0.0/src/cosmic_ray/tools/__init__.py +0 -0
- cosmic_ray-0.0.0/src/cosmic_ray/tools/badge.py +44 -0
- cosmic_ray-0.0.0/src/cosmic_ray/tools/filters/__init__.py +0 -0
- cosmic_ray-0.0.0/src/cosmic_ray/tools/filters/filter_app.py +74 -0
- cosmic_ray-0.0.0/src/cosmic_ray/tools/filters/git.py +96 -0
- cosmic_ray-0.0.0/src/cosmic_ray/tools/filters/operators_filter.py +69 -0
- cosmic_ray-0.0.0/src/cosmic_ray/tools/filters/pragma_no_mutate.py +66 -0
- cosmic_ray-0.0.0/src/cosmic_ray/tools/html.py +310 -0
- cosmic_ray-0.0.0/src/cosmic_ray/tools/http_workers.py +124 -0
- cosmic_ray-0.0.0/src/cosmic_ray/tools/report.py +57 -0
- cosmic_ray-0.0.0/src/cosmic_ray/tools/survival_rate.py +77 -0
- cosmic_ray-0.0.0/src/cosmic_ray/tools/xml.py +87 -0
- cosmic_ray-0.0.0/src/cosmic_ray/version.py +4 -0
- cosmic_ray-0.0.0/src/cosmic_ray/work_db.py +297 -0
- cosmic_ray-0.0.0/src/cosmic_ray/work_item.py +101 -0
- cosmic_ray-0.0.0/src/cosmic_ray.egg-info/PKG-INFO +108 -0
- cosmic_ray-0.0.0/src/cosmic_ray.egg-info/SOURCES.txt +62 -0
- cosmic_ray-0.0.0/src/cosmic_ray.egg-info/dependency_links.txt +1 -0
- cosmic_ray-0.0.0/src/cosmic_ray.egg-info/entry_points.txt +18 -0
- cosmic_ray-0.0.0/src/cosmic_ray.egg-info/requires.txt +28 -0
- cosmic_ray-0.0.0/src/cosmic_ray.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2015-2017 Sixty North AS
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
|
11
|
+
all copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: cosmic_ray
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Mutation testing
|
|
5
|
+
Author-email: Sixty North AS <austin@sixty-north.com>
|
|
6
|
+
License: Copyright (c) 2015-2017 Sixty North AS
|
|
7
|
+
|
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
10
|
+
in the Software without restriction, including without limitation the rights
|
|
11
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
13
|
+
furnished to do so, subject to the following conditions:
|
|
14
|
+
|
|
15
|
+
The above copyright notice and this permission notice shall be included in
|
|
16
|
+
all copies or substantial portions of the Software.
|
|
17
|
+
|
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
24
|
+
THE SOFTWARE.
|
|
25
|
+
|
|
26
|
+
Project-URL: repository, https://github.com/sixty-north/cosmic-ray
|
|
27
|
+
Classifier: Development Status :: 4 - Beta
|
|
28
|
+
Classifier: Environment :: Console
|
|
29
|
+
Classifier: Intended Audience :: Developers
|
|
30
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
31
|
+
Classifier: Operating System :: OS Independent
|
|
32
|
+
Classifier: Programming Language :: Python
|
|
33
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
39
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
40
|
+
Classifier: Topic :: Software Development :: Testing
|
|
41
|
+
Requires-Python: >=3.7
|
|
42
|
+
Description-Content-Type: text/x-rst
|
|
43
|
+
License-File: LICENCE.txt
|
|
44
|
+
Requires-Dist: aiohttp
|
|
45
|
+
Requires-Dist: anybadge
|
|
46
|
+
Requires-Dist: click
|
|
47
|
+
Requires-Dist: decorator
|
|
48
|
+
Requires-Dist: exit_codes
|
|
49
|
+
Requires-Dist: gitpython
|
|
50
|
+
Requires-Dist: parso
|
|
51
|
+
Requires-Dist: qprompt
|
|
52
|
+
Requires-Dist: rich
|
|
53
|
+
Requires-Dist: sqlalchemy
|
|
54
|
+
Requires-Dist: stevedore
|
|
55
|
+
Requires-Dist: toml
|
|
56
|
+
Requires-Dist: yattag
|
|
57
|
+
Provides-Extra: test
|
|
58
|
+
Requires-Dist: hypothesis; extra == "test"
|
|
59
|
+
Requires-Dist: pytest; extra == "test"
|
|
60
|
+
Requires-Dist: pytest-mock; extra == "test"
|
|
61
|
+
Provides-Extra: dev
|
|
62
|
+
Requires-Dist: flake8; extra == "dev"
|
|
63
|
+
Requires-Dist: flake8-pyproject; extra == "dev"
|
|
64
|
+
Requires-Dist: ruff; extra == "dev"
|
|
65
|
+
Requires-Dist: bump-my-version; extra == "dev"
|
|
66
|
+
Provides-Extra: docs
|
|
67
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
68
|
+
Requires-Dist: sphinx_rtd_theme; extra == "docs"
|
|
69
|
+
|
|
70
|
+
|Python version| |Python version windows| |Build Status| |Documentation|
|
|
71
|
+
|
|
72
|
+
Cosmic Ray: mutation testing for Python
|
|
73
|
+
=======================================
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
"Four human beings -- changed by space-born cosmic rays into something more than merely human."
|
|
77
|
+
|
|
78
|
+
-- The Fantastic Four
|
|
79
|
+
|
|
80
|
+
Cosmic Ray is a mutation testing tool for Python 3.
|
|
81
|
+
|
|
82
|
+
It makes small changes to your source code, running your test suite for each
|
|
83
|
+
one. Here's how the mutations look:
|
|
84
|
+
|
|
85
|
+
.. image:: docs/source/cr-in-action.gif
|
|
86
|
+
|
|
87
|
+
|full_documentation|_
|
|
88
|
+
|
|
89
|
+
Contributing
|
|
90
|
+
------------
|
|
91
|
+
|
|
92
|
+
The easiest way to contribute is to use Cosmic Ray and submit reports for defects or any other issues you come across.
|
|
93
|
+
Please see CONTRIBUTING.rst for more details.
|
|
94
|
+
|
|
95
|
+
.. |Python version| image:: https://img.shields.io/badge/Python_version-3.5+-blue.svg
|
|
96
|
+
:target: https://www.python.org/
|
|
97
|
+
.. |Python version windows| image:: https://img.shields.io/badge/Python_version_(windows)-3.7+-blue.svg
|
|
98
|
+
:target: https://www.python.org/
|
|
99
|
+
.. |Build Status| image:: https://github.com/sixty-north/cosmic-ray/actions/workflows/python-package.yml/badge.svg
|
|
100
|
+
:target: https://github.com/sixty-north/cosmic-ray/actions/workflows/python-package.yml
|
|
101
|
+
.. |Code Health| image:: https://landscape.io/github/sixty-north/cosmic-ray/master/landscape.svg?style=flat
|
|
102
|
+
:target: https://landscape.io/github/sixty-north/cosmic-ray/master
|
|
103
|
+
.. |Code Coverage| image:: https://codecov.io/gh/sixty-north/cosmic-ray/branch/master/graph/badge.svg
|
|
104
|
+
:target: https://codecov.io/gh/Vimjas/covimerage/branch/master
|
|
105
|
+
.. |Documentation| image:: https://readthedocs.org/projects/cosmic-ray/badge/?version=latest
|
|
106
|
+
:target: http://cosmic-ray.readthedocs.org/en/latest/
|
|
107
|
+
.. |full_documentation| replace:: **Read the full documentation at readthedocs.**
|
|
108
|
+
.. _full_documentation: http://cosmic-ray.readthedocs.org/en/latest/
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
|Python version| |Python version windows| |Build Status| |Documentation|
|
|
2
|
+
|
|
3
|
+
Cosmic Ray: mutation testing for Python
|
|
4
|
+
=======================================
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
"Four human beings -- changed by space-born cosmic rays into something more than merely human."
|
|
8
|
+
|
|
9
|
+
-- The Fantastic Four
|
|
10
|
+
|
|
11
|
+
Cosmic Ray is a mutation testing tool for Python 3.
|
|
12
|
+
|
|
13
|
+
It makes small changes to your source code, running your test suite for each
|
|
14
|
+
one. Here's how the mutations look:
|
|
15
|
+
|
|
16
|
+
.. image:: docs/source/cr-in-action.gif
|
|
17
|
+
|
|
18
|
+
|full_documentation|_
|
|
19
|
+
|
|
20
|
+
Contributing
|
|
21
|
+
------------
|
|
22
|
+
|
|
23
|
+
The easiest way to contribute is to use Cosmic Ray and submit reports for defects or any other issues you come across.
|
|
24
|
+
Please see CONTRIBUTING.rst for more details.
|
|
25
|
+
|
|
26
|
+
.. |Python version| image:: https://img.shields.io/badge/Python_version-3.5+-blue.svg
|
|
27
|
+
:target: https://www.python.org/
|
|
28
|
+
.. |Python version windows| image:: https://img.shields.io/badge/Python_version_(windows)-3.7+-blue.svg
|
|
29
|
+
:target: https://www.python.org/
|
|
30
|
+
.. |Build Status| image:: https://github.com/sixty-north/cosmic-ray/actions/workflows/python-package.yml/badge.svg
|
|
31
|
+
:target: https://github.com/sixty-north/cosmic-ray/actions/workflows/python-package.yml
|
|
32
|
+
.. |Code Health| image:: https://landscape.io/github/sixty-north/cosmic-ray/master/landscape.svg?style=flat
|
|
33
|
+
:target: https://landscape.io/github/sixty-north/cosmic-ray/master
|
|
34
|
+
.. |Code Coverage| image:: https://codecov.io/gh/sixty-north/cosmic-ray/branch/master/graph/badge.svg
|
|
35
|
+
:target: https://codecov.io/gh/Vimjas/covimerage/branch/master
|
|
36
|
+
.. |Documentation| image:: https://readthedocs.org/projects/cosmic-ray/badge/?version=latest
|
|
37
|
+
:target: http://cosmic-ray.readthedocs.org/en/latest/
|
|
38
|
+
.. |full_documentation| replace:: **Read the full documentation at readthedocs.**
|
|
39
|
+
.. _full_documentation: http://cosmic-ray.readthedocs.org/en/latest/
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ['setuptools']
|
|
3
|
+
build-backend = 'setuptools.build_meta'
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cosmic_ray"
|
|
7
|
+
requires-python = ">= 3.7"
|
|
8
|
+
dynamic = ["version"]
|
|
9
|
+
authors = [{ name = "Sixty North AS", email = "austin@sixty-north.com" }]
|
|
10
|
+
description = "Mutation testing"
|
|
11
|
+
readme = { file = "README.rst", content-type = "text/x-rst" }
|
|
12
|
+
license = { file = "LICENCE.txt" }
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Environment :: Console",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python",
|
|
20
|
+
"Programming Language :: Python :: 3.7",
|
|
21
|
+
"Programming Language :: Python :: 3.8",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
27
|
+
"Topic :: Software Development :: Testing",
|
|
28
|
+
|
|
29
|
+
]
|
|
30
|
+
dependencies = [
|
|
31
|
+
"aiohttp",
|
|
32
|
+
"anybadge",
|
|
33
|
+
"click",
|
|
34
|
+
"decorator",
|
|
35
|
+
"exit_codes",
|
|
36
|
+
"gitpython",
|
|
37
|
+
"parso",
|
|
38
|
+
"qprompt",
|
|
39
|
+
"rich",
|
|
40
|
+
"sqlalchemy",
|
|
41
|
+
"stevedore",
|
|
42
|
+
"toml",
|
|
43
|
+
"yattag",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[project.optional-dependencies]
|
|
47
|
+
test = ["hypothesis", "pytest", "pytest-mock"]
|
|
48
|
+
dev = ["flake8", "flake8-pyproject", "ruff", "bump-my-version"]
|
|
49
|
+
docs = ["sphinx", "sphinx_rtd_theme"]
|
|
50
|
+
|
|
51
|
+
[project.scripts]
|
|
52
|
+
cosmic-ray = "cosmic_ray.cli:main"
|
|
53
|
+
cr-html = "cosmic_ray.tools.html:report_html"
|
|
54
|
+
cr-report = "cosmic_ray.tools.report:report"
|
|
55
|
+
cr-badge = "cosmic_ray.tools.badge:generate_badge"
|
|
56
|
+
cr-rate = "cosmic_ray.tools.survival_rate:format_survival_rate"
|
|
57
|
+
cr-xml = "cosmic_ray.tools.xml:report_xml"
|
|
58
|
+
cr-filter-operators = "cosmic_ray.tools.filters.operators_filter:main"
|
|
59
|
+
cr-filter-pragma = "cosmic_ray.tools.filters.pragma_no_mutate:main"
|
|
60
|
+
cr-filter-git = "cosmic_ray.tools.filters.git:main"
|
|
61
|
+
cr-http-workers = "cosmic_ray.tools.http_workers:main"
|
|
62
|
+
|
|
63
|
+
[project.entry-points."cosmic_ray.operator_providers"]
|
|
64
|
+
core = "cosmic_ray.operators.provider:OperatorProvider"
|
|
65
|
+
|
|
66
|
+
[project.entry-points."cosmic_ray.distributors"]
|
|
67
|
+
http = "cosmic_ray.distribution.http:HttpDistributor"
|
|
68
|
+
local = "cosmic_ray.distribution.local:LocalDistributor"
|
|
69
|
+
|
|
70
|
+
[project.urls]
|
|
71
|
+
repository = "https://github.com/sixty-north/cosmic-ray"
|
|
72
|
+
|
|
73
|
+
[tool.setuptools.packages.find]
|
|
74
|
+
where = ["src"]
|
|
75
|
+
|
|
76
|
+
[tool.bumpversion]
|
|
77
|
+
current_version = "8.3.9"
|
|
78
|
+
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
|
79
|
+
serialize = ["{major}.{minor}.{patch}"]
|
|
80
|
+
tag = true
|
|
81
|
+
commit = true
|
|
82
|
+
message = "Bump version: {current_version} → {new_version}"
|
|
83
|
+
tag_name = "release/v{new_version}"
|
|
84
|
+
tag_message = "Bump version: {current_version} → {new_version}"
|
|
85
|
+
|
|
86
|
+
[[tool.bumpversion.files]]
|
|
87
|
+
filename = "src/cosmic_ray/version.py"
|
|
88
|
+
|
|
89
|
+
[tool.flake8]
|
|
90
|
+
max-line-length = 120
|
|
91
|
+
|
|
92
|
+
[tool.ruff]
|
|
93
|
+
line-length = 120
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Cosmic Ray is a mutation testing tool for Python."""
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"Tools for working with parso ASTs."
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
import io
|
|
5
|
+
|
|
6
|
+
import parso.python.tree
|
|
7
|
+
import parso.tree
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Visitor(ABC):
|
|
11
|
+
"""AST visitor for parso trees.
|
|
12
|
+
|
|
13
|
+
This supports both simple traversal as well as editing of the tree.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def walk(self, node):
|
|
17
|
+
"Walk a parse tree, calling visit for each node."
|
|
18
|
+
node = self.visit(node)
|
|
19
|
+
|
|
20
|
+
if node is None:
|
|
21
|
+
return None
|
|
22
|
+
|
|
23
|
+
if isinstance(node, parso.tree.BaseNode):
|
|
24
|
+
walked = map(self.walk, node.children)
|
|
25
|
+
node.children = [child for child in walked if child is not None]
|
|
26
|
+
|
|
27
|
+
return node
|
|
28
|
+
|
|
29
|
+
@abstractmethod
|
|
30
|
+
def visit(self, node):
|
|
31
|
+
"""Called for each node in the walk.
|
|
32
|
+
|
|
33
|
+
This should return a node that will replace the node argument in the AST. This can be
|
|
34
|
+
the node argument itself, a new node, or None. If None is returned, then the node is
|
|
35
|
+
removed from the tree.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
node: The node currently being visited.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
A node or `None`.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def ast_nodes(node):
|
|
46
|
+
"""Iterable of all nodes in a tree.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
node: The top node in a parso tree to iterate.
|
|
50
|
+
|
|
51
|
+
Yields:
|
|
52
|
+
All of the nodes in the tree.
|
|
53
|
+
"""
|
|
54
|
+
yield node
|
|
55
|
+
|
|
56
|
+
if isinstance(node, parso.tree.BaseNode):
|
|
57
|
+
for child in node.children:
|
|
58
|
+
yield from ast_nodes(child)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_ast(module_path):
|
|
62
|
+
"""Get the AST for the code in a file.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
module_path: pathlib.Path to the file containing the code.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
The parso parse tree for the code in `module_path`.
|
|
69
|
+
"""
|
|
70
|
+
with module_path.open(mode="rt", encoding="utf-8") as handle:
|
|
71
|
+
source = handle.read()
|
|
72
|
+
|
|
73
|
+
return parso.parse(source)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def is_none(node):
|
|
77
|
+
"Determine if a node is the `None` keyword."
|
|
78
|
+
return isinstance(node, parso.python.tree.Keyword) and node.value == "None"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def is_number(node):
|
|
82
|
+
"Determine if a node is a number."
|
|
83
|
+
return isinstance(node, parso.python.tree.Number)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def dump_node(node):
|
|
87
|
+
"Generate string version of node."
|
|
88
|
+
buffer = io.StringIO()
|
|
89
|
+
write = buffer.write
|
|
90
|
+
|
|
91
|
+
def do_dump(node, indent=""):
|
|
92
|
+
write("{}{}({}".format(indent, type(node).__name__, node.type))
|
|
93
|
+
value = getattr(node, "value", None)
|
|
94
|
+
if value:
|
|
95
|
+
value = value.replace("\n", "\\n")
|
|
96
|
+
write(", '{}'".format(value))
|
|
97
|
+
children = getattr(node, "children", None)
|
|
98
|
+
if children:
|
|
99
|
+
write(", [\n")
|
|
100
|
+
for child in children:
|
|
101
|
+
do_dump(child, indent + " " * 4)
|
|
102
|
+
write(",\n")
|
|
103
|
+
write("{}]".format(indent))
|
|
104
|
+
write(")")
|
|
105
|
+
if not indent:
|
|
106
|
+
write("\n")
|
|
107
|
+
|
|
108
|
+
do_dump(node)
|
|
109
|
+
return buffer.getvalue()
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"Tools for querying ASTs."
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ASTQuery:
|
|
5
|
+
"""
|
|
6
|
+
Allowing to navigate into any object and test attribute of any object:
|
|
7
|
+
|
|
8
|
+
Examples:
|
|
9
|
+
>>> ASTQuery(node).parent.match(Node, type='node').ok
|
|
10
|
+
|
|
11
|
+
Test if node.parent isinstance of Node and node.parent.type == 'node'
|
|
12
|
+
At each step (each '.' (dot)) you receive an ObjTest object, then
|
|
13
|
+
|
|
14
|
+
Navigation:
|
|
15
|
+
You can call any properties or functions of the base object
|
|
16
|
+
>>> ASTQuery(node).parent.children[2].get_next_sibling()
|
|
17
|
+
|
|
18
|
+
Test:
|
|
19
|
+
>>> ASTQuery(node).match(attr='value').match(Class)
|
|
20
|
+
All in once:
|
|
21
|
+
>>> ASTQuery(node).match(Class, attr='value')
|
|
22
|
+
|
|
23
|
+
Conditional navigation:
|
|
24
|
+
>>> ASTQuery(node).IF.match(attr='intermediate').parent.FI
|
|
25
|
+
|
|
26
|
+
Final result:
|
|
27
|
+
>>> ASTQuery(node).ok
|
|
28
|
+
>>> bool(ASTQuery(node))
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self, obj):
|
|
33
|
+
self.obj = obj
|
|
34
|
+
|
|
35
|
+
def _clone(self, obj) -> "ASTQuery":
|
|
36
|
+
"Clone this query."
|
|
37
|
+
return type(self)(obj)
|
|
38
|
+
|
|
39
|
+
def match(self, cls=None, **kwargs) -> "ASTQuery":
|
|
40
|
+
"Check if node matches a class."
|
|
41
|
+
obj = self.obj
|
|
42
|
+
if obj is None:
|
|
43
|
+
return self
|
|
44
|
+
|
|
45
|
+
if cls is None or isinstance(obj, cls):
|
|
46
|
+
for k, v in kwargs.items():
|
|
47
|
+
op = None
|
|
48
|
+
k__op = k.split("__")
|
|
49
|
+
if len(k__op) == 2:
|
|
50
|
+
k, op = k__op
|
|
51
|
+
node_value = getattr(obj, k)
|
|
52
|
+
if op is None:
|
|
53
|
+
if node_value != v:
|
|
54
|
+
break
|
|
55
|
+
elif op == "in":
|
|
56
|
+
if node_value not in v:
|
|
57
|
+
break
|
|
58
|
+
else:
|
|
59
|
+
raise ValueError("Can't handle operator {}".format(op))
|
|
60
|
+
else:
|
|
61
|
+
# All is true, continue recursion
|
|
62
|
+
return self
|
|
63
|
+
|
|
64
|
+
# A test fails
|
|
65
|
+
return self._clone(None)
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def ok(self):
|
|
69
|
+
"Is the query ok."
|
|
70
|
+
return bool(self.obj)
|
|
71
|
+
|
|
72
|
+
def __bool__(self):
|
|
73
|
+
return self.ok
|
|
74
|
+
|
|
75
|
+
def __getattr__(self, item) -> "ASTQuery":
|
|
76
|
+
obj = self.obj
|
|
77
|
+
if obj is None:
|
|
78
|
+
return self
|
|
79
|
+
return self._clone(getattr(obj, item))
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def IF(self):
|
|
83
|
+
"Conditional navigation."
|
|
84
|
+
return ASTQueryOptional(self.obj, obj_test=self)
|
|
85
|
+
|
|
86
|
+
def __call__(self, *args, **kwargs) -> "ASTQuery":
|
|
87
|
+
if self.obj is None:
|
|
88
|
+
return self
|
|
89
|
+
return self._clone(self.obj(*args, **kwargs))
|
|
90
|
+
|
|
91
|
+
def __getitem__(self, item) -> "ASTQuery":
|
|
92
|
+
if self.obj is None:
|
|
93
|
+
return self
|
|
94
|
+
return self._clone(self.obj[item])
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class ASTQueryOptional(ASTQuery):
|
|
98
|
+
"Manages conditional navigation."
|
|
99
|
+
|
|
100
|
+
def __init__(self, obj, obj_test=None):
|
|
101
|
+
super().__init__(obj)
|
|
102
|
+
self._initial = obj_test
|
|
103
|
+
|
|
104
|
+
def _clone(self, obj):
|
|
105
|
+
o = super()._clone(obj)
|
|
106
|
+
o._initial = self._initial # pylint: disable=protected-access
|
|
107
|
+
|
|
108
|
+
return o
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def FI(self):
|
|
112
|
+
"End of conditional navigation."
|
|
113
|
+
if self:
|
|
114
|
+
return self._initial._clone(self.obj) # pylint: disable=protected-access
|
|
115
|
+
return self._initial
|