codegrapher 0.2.0__tar.gz → 0.3.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.
Files changed (38) hide show
  1. codegrapher-0.3.0/.gitattributes +1 -0
  2. codegrapher-0.3.0/.github/workflows/main.yaml +39 -0
  3. codegrapher-0.3.0/.github/workflows/publish.yaml +21 -0
  4. codegrapher-0.3.0/.gitignore +13 -0
  5. codegrapher-0.3.0/.readthedocs.yaml +22 -0
  6. codegrapher-0.3.0/LICENSE +16 -0
  7. codegrapher-0.3.0/Makefile +16 -0
  8. codegrapher-0.3.0/PKG-INFO +150 -0
  9. {codegrapher-0.2.0 → codegrapher-0.3.0}/README.rst +25 -6
  10. {codegrapher-0.2.0 → codegrapher-0.3.0}/cli/script.py +5 -1
  11. codegrapher-0.3.0/codegrapher/__init__.py +1 -0
  12. {codegrapher-0.2.0 → codegrapher-0.3.0}/codegrapher/graph.py +31 -11
  13. {codegrapher-0.2.0 → codegrapher-0.3.0}/codegrapher/parser.py +104 -29
  14. codegrapher-0.3.0/docs/Makefile +177 -0
  15. codegrapher-0.3.0/docs/codegrapher.rst +29 -0
  16. codegrapher-0.3.0/docs/conf.py +261 -0
  17. codegrapher-0.3.0/docs/docs-requirements.txt +3 -0
  18. codegrapher-0.3.0/docs/index.rst +25 -0
  19. codegrapher-0.3.0/docs/make.bat +242 -0
  20. codegrapher-0.3.0/docs/modules.rst +7 -0
  21. codegrapher-0.3.0/docs/readme_link.rst +3 -0
  22. codegrapher-0.3.0/pyproject.toml +53 -0
  23. codegrapher-0.3.0/setup.md +38 -0
  24. {codegrapher-0.2.0 → codegrapher-0.3.0}/tests/test_graph.py +0 -3
  25. {codegrapher-0.2.0 → codegrapher-0.3.0}/tests/test_parser.py +101 -14
  26. codegrapher-0.3.0/uv.lock +691 -0
  27. codegrapher-0.2.0/PKG-INFO +0 -16
  28. codegrapher-0.2.0/codegrapher/__init__.py +0 -1
  29. codegrapher-0.2.0/codegrapher.egg-info/PKG-INFO +0 -16
  30. codegrapher-0.2.0/codegrapher.egg-info/SOURCES.txt +0 -16
  31. codegrapher-0.2.0/codegrapher.egg-info/dependency_links.txt +0 -1
  32. codegrapher-0.2.0/codegrapher.egg-info/entry_points.txt +0 -4
  33. codegrapher-0.2.0/codegrapher.egg-info/requires.txt +0 -2
  34. codegrapher-0.2.0/codegrapher.egg-info/top_level.txt +0 -3
  35. codegrapher-0.2.0/setup.cfg +0 -5
  36. codegrapher-0.2.0/setup.py +0 -32
  37. {codegrapher-0.2.0 → codegrapher-0.3.0}/cli/__init__.py +0 -0
  38. {codegrapher-0.2.0 → codegrapher-0.3.0}/tests/__init__.py +0 -0
@@ -0,0 +1 @@
1
+ * text=auto eol=lf
@@ -0,0 +1,39 @@
1
+ name: Codegrapher unit tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ build:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
14
+ fail-fast: false
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - name: Set up Python ${{ matrix.python-version }}
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: ${{ matrix.python-version }}
22
+ - name: Install uv
23
+ uses: astral-sh/setup-uv@v5
24
+ - name: Install project and dev dependencies
25
+ run: uv sync --locked --extra dev
26
+ - name: Test with pytest
27
+ run: uv run pytest tests --doctest-modules --junitxml=junit/test-results-${{ matrix.python-version }}.xml --cov --cov-report=xml --cov-report=html
28
+ - name: Upload test results
29
+ uses: actions/upload-artifact@v4
30
+ if: always()
31
+ with:
32
+ name: test-results-${{ matrix.python-version }}
33
+ path: junit/test-results-${{ matrix.python-version }}.xml
34
+ - name: Upload coverage report
35
+ uses: actions/upload-artifact@v4
36
+ if: always()
37
+ with:
38
+ name: coverage-report-${{ matrix.python-version }}
39
+ path: htmlcov/
@@ -0,0 +1,21 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ environment: release
11
+ permissions:
12
+ id-token: write # required for OIDC trusted publishing
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - name: Install uv
17
+ uses: astral-sh/setup-uv@v5
18
+ - name: Build distribution
19
+ run: uv build
20
+ - name: Publish to PyPI
21
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,13 @@
1
+ .idea/
2
+ codegrapher.egg-info
3
+ *.pyc
4
+ .coverage
5
+ cover/
6
+ dist/
7
+ docs/_build/
8
+
9
+ *.gv
10
+ *.pdf
11
+ *.png
12
+
13
+ .python-version
@@ -0,0 +1,22 @@
1
+ # .readthedocs.yaml
2
+ # Read the Docs configuration file
3
+ # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
4
+
5
+ # Required
6
+ version: 2
7
+
8
+ # Set the version of Python and other tools you might need
9
+ build:
10
+ os: ubuntu-22.04
11
+ tools:
12
+ python: "3.11"
13
+
14
+ # Build documentation in the docs/ directory with Sphinx
15
+ sphinx:
16
+ configuration: docs/conf.py
17
+
18
+ # We recommend specifying your dependencies to enable reproducible builds:
19
+ # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
20
+ # python:
21
+ # install:
22
+ # - requirements: docs/requirements.txt
@@ -0,0 +1,16 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Laura Rupprecht
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6
+ documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
7
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
8
+ persons to whom the Software is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
11
+ Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
14
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,16 @@
1
+ .PHONY: install install-dev test build check
2
+
3
+ install:
4
+ uv sync
5
+
6
+ install-dev:
7
+ uv sync --extra dev
8
+
9
+ test:
10
+ uv run pytest
11
+
12
+ build:
13
+ uv build
14
+
15
+ check:
16
+ twine check dist/*
@@ -0,0 +1,150 @@
1
+ Metadata-Version: 2.4
2
+ Name: codegrapher
3
+ Version: 0.3.0
4
+ Summary: Code that graphs code
5
+ Project-URL: Homepage, https://github.com/LaurEars/codegrapher
6
+ Project-URL: Repository, https://github.com/LaurEars/codegrapher
7
+ Project-URL: Issues, https://github.com/LaurEars/codegrapher/issues
8
+ Author: Laura Rupprecht
9
+ License: The MIT License (MIT)
10
+
11
+ Copyright (c) 2026 Laura Rupprecht
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
14
+ documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
15
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
16
+ persons to whom the Software is furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
19
+ Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
22
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
23
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+ License-File: LICENSE
26
+ Keywords: ast,call-graph,code,documentation,graph,graphviz
27
+ Classifier: Development Status :: 4 - Beta
28
+ Classifier: Intended Audience :: Developers
29
+ Classifier: License :: OSI Approved :: MIT License
30
+ Classifier: Programming Language :: Python :: 3
31
+ Classifier: Programming Language :: Python :: 3.8
32
+ Classifier: Programming Language :: Python :: 3.9
33
+ Classifier: Programming Language :: Python :: 3.10
34
+ Classifier: Programming Language :: Python :: 3.11
35
+ Classifier: Programming Language :: Python :: 3.12
36
+ Classifier: Programming Language :: Python :: 3.13
37
+ Classifier: Topic :: Software Development :: Documentation
38
+ Requires-Python: >=3.8
39
+ Requires-Dist: click>=7.0
40
+ Requires-Dist: graphviz>=0.4.2
41
+ Provides-Extra: dev
42
+ Requires-Dist: coverage>=5.0; extra == 'dev'
43
+ Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
44
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
45
+ Description-Content-Type: text/x-rst
46
+
47
+ codegrapher
48
+ ===========
49
+
50
+ .. image:: https://github.com/LaurEars/codegrapher/actions/workflows/main.yaml/badge.svg
51
+ :target: https://github.com/LaurEars/codegrapher/actions/workflows/main.yaml
52
+
53
+
54
+ Code that graphs code
55
+ ---------------------
56
+ Uses the python `AST <https://docs.python.org/3/library/ast.html>`_ to parse Python source code and build a call graph.
57
+
58
+
59
+ Output
60
+ ------
61
+ An example of the current output of the parser parsing itself.
62
+
63
+ .. image:: docs/codegrapher.png
64
+ :target: docs/codegrapher.png
65
+ :align: center
66
+ :width: 100 %
67
+ :alt: parser.py
68
+
69
+
70
+ Installation
71
+ ------------
72
+
73
+ .. code:: bash
74
+
75
+ pip install codegrapher
76
+
77
+
78
+ To generate graphs, `graphviz <http://www.graphviz.org/Download.php>`_ must be installed.
79
+
80
+
81
+ Usage
82
+ -----
83
+
84
+ At the command line
85
+ ~~~~~~~~~~~~~~~~~~~
86
+ To parse a file and output results to the console:
87
+
88
+ .. code:: bash
89
+
90
+ codegrapher path/to/file.py --printed
91
+
92
+
93
+ To parse a file and output results to a file:
94
+
95
+ .. code:: bash
96
+
97
+ codegrapher path/to/file.py --output output_file_name --output-type png
98
+
99
+ To analyze a directory of files, along with all files it contains:
100
+
101
+ .. code:: bash
102
+
103
+ codegrapher -r path/to/directory --output multiple_file_analysis
104
+
105
+ And if you have a list of functions that aren't useful in your graph, add it to a `.cg_ignore` file:
106
+
107
+ ::
108
+
109
+ # cg_ignore file
110
+ # all lines beginning with '#' are ignored
111
+
112
+ # every function calls this, so it's not helpful in my graph:
113
+ log_error
114
+
115
+ # I don't want to see this in my graph:
116
+ parse
117
+ lower
118
+
119
+ Then add the `--ignore` flag to your command. Using the flag `--remove-builtins` provides the same functionality
120
+ for ignoring items found in `__builtins__`.
121
+
122
+ As a Python module
123
+ ~~~~~~~~~~~~~~~~~~
124
+
125
+ To easily parse code in Python :
126
+
127
+ .. code:: python
128
+
129
+ from codegrapher.parser import FileObject
130
+
131
+ file_object = FileObject('path/to/file.py')
132
+ file_object.visit()
133
+
134
+ And then to add that code to a graph and render it (using graphviz):
135
+
136
+ .. code:: python
137
+
138
+ from codegrapher.graph import FunctionGrapher
139
+
140
+ graph = FunctionGrapher()
141
+ graph.add_file_to_graph(file_object)
142
+ graph.name = 'name.gv'
143
+ graph.format = 'png'
144
+ graph.render()
145
+
146
+ Which will produce your code as a png file, `name.gv.png`, along with a
147
+ `dot file <http://en.wikipedia.org/wiki/DOT_%28graph_description_language%29>`_ `name.gv`
148
+
149
+ More documentation for the Python module can be found at
150
+ `Read the Docs <http://codegrapher.readthedocs.org/en/latest/>`_.
@@ -1,21 +1,21 @@
1
1
  codegrapher
2
2
  ===========
3
3
 
4
- .. image:: https://travis-ci.org/LaurEars/codegrapher.svg?branch=master
5
- :target: https://travis-ci.org/LaurEars/codegrapher
4
+ .. image:: https://github.com/LaurEars/codegrapher/actions/workflows/main.yaml/badge.svg
5
+ :target: https://github.com/LaurEars/codegrapher/actions/workflows/main.yaml
6
6
 
7
7
 
8
8
  Code that graphs code
9
9
  ---------------------
10
- Uses the python `AST <https://docs.python.org/2/library/ast.html>`_ to parse Python source code and build a call graph.
10
+ Uses the python `AST <https://docs.python.org/3/library/ast.html>`_ to parse Python source code and build a call graph.
11
11
 
12
12
 
13
13
  Output
14
14
  ------
15
15
  An example of the current output of the parser parsing itself.
16
16
 
17
- .. image:: http://i.imgur.com/QMES0Na.png
18
- :target: http://i.imgur.com/QMES0Na.png
17
+ .. image:: docs/codegrapher.png
18
+ :target: docs/codegrapher.png
19
19
  :align: center
20
20
  :width: 100 %
21
21
  :alt: parser.py
@@ -56,6 +56,22 @@ To analyze a directory of files, along with all files it contains:
56
56
 
57
57
  codegrapher -r path/to/directory --output multiple_file_analysis
58
58
 
59
+ And if you have a list of functions that aren't useful in your graph, add it to a `.cg_ignore` file:
60
+
61
+ ::
62
+
63
+ # cg_ignore file
64
+ # all lines beginning with '#' are ignored
65
+
66
+ # every function calls this, so it's not helpful in my graph:
67
+ log_error
68
+
69
+ # I don't want to see this in my graph:
70
+ parse
71
+ lower
72
+
73
+ Then add the `--ignore` flag to your command. Using the flag `--remove-builtins` provides the same functionality
74
+ for ignoring items found in `__builtins__`.
59
75
 
60
76
  As a Python module
61
77
  ~~~~~~~~~~~~~~~~~~
@@ -82,4 +98,7 @@ And then to add that code to a graph and render it (using graphviz):
82
98
  graph.render()
83
99
 
84
100
  Which will produce your code as a png file, `name.gv.png`, along with a
85
- `dot file <http://en.wikipedia.org/wiki/DOT_%28graph_description_language%29>`_ `name.gv`
101
+ `dot file <http://en.wikipedia.org/wiki/DOT_%28graph_description_language%29>`_ `name.gv`
102
+
103
+ More documentation for the Python module can be found at
104
+ `Read the Docs <http://codegrapher.readthedocs.org/en/latest/>`_.
@@ -12,10 +12,11 @@ from codegrapher.parser import FileObject
12
12
  @click.option('-r', '--recursive', default=False, is_flag=True,
13
13
  help='Treat code argument as a directory and parse all files in directory, recursively')
14
14
  @click.option('--printed', default=False, is_flag=True, help='Pretty prints the call tree for each class in the file')
15
+ @click.option('--ignore', default=False, is_flag=True, help='Use a .cg_ignore file to ignore functions in call tree')
15
16
  @click.option('--remove-builtins', default=False, is_flag=True, help='Removes builtin functions from call trees')
16
17
  @click.option('--output', help='Graphviz output file name')
17
18
  @click.option('--output-format', default='pdf', help='File type for graphviz output file')
18
- def cli(code, recursive, printed, remove_builtins, output, output_format):
19
+ def cli(code, recursive, printed, ignore, remove_builtins, output, output_format):
19
20
  """
20
21
  Parses a file.
21
22
  codegrapher [file_name]
@@ -35,6 +36,9 @@ def cli(code, recursive, printed, remove_builtins, output, output_format):
35
36
  file_object.visit()
36
37
  if remove_builtins:
37
38
  file_object.remove_builtins()
39
+ if ignore:
40
+ file_object.add_ignore_file()
41
+ file_object.ignore_functions()
38
42
  if printed:
39
43
  click.echo('Classes in file {}:'.format(file_name))
40
44
  for class_object in file_object.classes:
@@ -0,0 +1 @@
1
+ __version__ = '0.3.0'
@@ -13,12 +13,14 @@ class Node(object):
13
13
  Optimized to handle nodes that represent functions in a program.
14
14
 
15
15
  Attributes:
16
- tuple (tuple): Contains the namespace, class, and function name for the current node.
16
+ tuple (tuple): Contains the namespace, class, and function name for the current node. If namespace is an empty
17
+ string, this contains just the class and function names. If a string is provided to the constructor this
18
+ is a tuple containing just the function name.
17
19
  """
18
20
  def __init__(self, input_node):
19
21
  if isinstance(input_node, tuple):
20
22
  if input_node[0] == '':
21
- self.tuple = input_node[1:-1]
23
+ self.tuple = input_node[1:]
22
24
  else:
23
25
  self.tuple = input_node
24
26
  else:
@@ -88,6 +90,8 @@ class FunctionGrapher(object):
88
90
  Arguments:
89
91
  class_names (list): List of class names to be recognized by the graph as `class_name.__init__` nodes.
90
92
  dictionary (dict): `ClassObject.call_tree` dict to be added to graph nodes and edges.
93
+ relative_namespace (string): Relative namespace for the current class, i.e. where the current class is
94
+ located relative to the root, in dotted path notation.
91
95
  """
92
96
  # todo: better handle project hierarchy by looking at imports
93
97
  # add nodes
@@ -116,20 +120,36 @@ class FunctionGrapher(object):
116
120
 
117
121
  Arguments:
118
122
  classes (list): list of :class:`codegrapher.parser.ClassObject` items.
123
+ relative_namespace (string): namespace of the current class.
119
124
  """
120
- # todo: separate class methods from instance methods
121
125
  for cls in classes:
122
- functions = set(fcn.name for fcn in cls.functions)
123
- if '__init__' in functions:
126
+ # If a class is here, add it as a node
127
+ self.nodes.add(Node((relative_namespace, cls.name)))
128
+
129
+ if not all((fcn.is_classmethod for fcn in cls.functions)):
130
+ # for case where there is at least one non-classmethod, assume implied (or explicit) __init__
131
+
132
+ # make a node between the class name and __init__
124
133
  self.nodes.add(Node((relative_namespace, cls.name, '__init__')))
125
- for fcn in functions:
126
- if fcn == '__init__':
127
- continue # skip the case where init would refer back to itself
128
- self.edges.add((Node((relative_namespace, cls.name, '__init__')),
129
- Node((relative_namespace, cls.name, fcn))))
134
+ self.edges.add((Node((relative_namespace, cls.name)), Node((relative_namespace, cls.name, '__init__'))))
135
+ for fcn in cls.functions:
136
+ # skip classmethods and case where init would refer back to itself
137
+ if not fcn.is_classmethod and not fcn.name == '__init__':
138
+ self.edges.add((Node((relative_namespace, cls.name, '__init__')),
139
+ Node((relative_namespace, cls.name, fcn.name))))
140
+ elif fcn.is_classmethod:
141
+ self.edges.add((Node((relative_namespace, cls.name)),
142
+ Node((relative_namespace, cls.name, fcn.name))))
143
+
144
+ else:
145
+ # for the case where there are only classmethods defined
146
+ for fcn in cls.functions:
147
+ self.edges.add((Node((relative_namespace, cls.name)),
148
+ Node((relative_namespace, cls.name, fcn.name))))
130
149
 
131
150
  def render(self, name=None):
132
- """ Renders the current graph.
151
+ """ Renders the current graph. `Graphviz <http://www.graphviz.org/>`_ must be installed for the graph to be
152
+ rendered.
133
153
 
134
154
  Arguments:
135
155
  name (string): filename to override `self.name`.