├── .coveragerc ├── .gitignore ├── .travis.yml ├── AUTHORS.rst ├── CHANGELOG.rst ├── CONTRIBUTING.rst ├── LICENSE ├── README.rst ├── docs ├── Makefile ├── _static │ └── images │ │ ├── django.db.png │ │ └── flask.png ├── authors.rst ├── changelog.rst ├── conf.py ├── contributing.rst ├── index.rst ├── make.bat └── requirements.txt ├── setup.cfg ├── setup.py ├── src └── impulse │ ├── __init__.py │ ├── application │ ├── __init__.py │ └── use_cases.py │ └── cli.py ├── tests ├── __init__.py └── test_placeholder.py └── tox.ini /.coveragerc: -------------------------------------------------------------------------------- 1 | [paths] 2 | source = 3 | src 4 | */site-packages 5 | 6 | [run] 7 | branch = true 8 | source = 9 | impulse 10 | tests 11 | parallel = true 12 | 13 | [report] 14 | show_missing = true 15 | precision = 2 16 | omit = *migrations* 17 | exclude_lines = 18 | pragma: nocover 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | .eggs 13 | parts 14 | bin 15 | var 16 | sdist 17 | wheelhouse 18 | develop-eggs 19 | .installed.cfg 20 | lib 21 | lib64 22 | venv*/ 23 | pyvenv*/ 24 | 25 | # Installer logs 26 | pip-log.txt 27 | 28 | # Unit test / coverage reports 29 | .coverage 30 | .tox 31 | .coverage.* 32 | .pytest_cache/ 33 | nosetests.xml 34 | coverage.xml 35 | htmlcov 36 | 37 | # Translations 38 | *.mo 39 | 40 | # Mr Developer 41 | .mr.developer.cfg 42 | .project 43 | .pydevproject 44 | .idea 45 | *.iml 46 | *.komodoproject 47 | 48 | # Complexity 49 | output/*.html 50 | output/*/index.html 51 | 52 | # Sphinx 53 | docs/_build 54 | 55 | .DS_Store 56 | *~ 57 | .*.sw[po] 58 | .build 59 | .ve 60 | .env 61 | .cache 62 | .pytest 63 | .bootstrap 64 | .appveyor.token 65 | *.bak 66 | 67 | # Mypy Cache 68 | .mypy_cache/ 69 | 70 | .python-version 71 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | dist: jammy 3 | cache: pip 4 | jobs: 5 | include: 6 | - python: '3.7' 7 | env: 8 | - TOXENV=py37,report 9 | - python: '3.8' 10 | env: 11 | - TOXENV=py38,report 12 | - python: '3.9.0' 13 | env: 14 | - TOXENV=py39,report 15 | - python: '3.10.5' 16 | env: 17 | - TOXENV=py310,report,check,docs 18 | addons: 19 | apt: 20 | packages: 21 | - graphviz 22 | homebrew: 23 | packages: 24 | - graphviz 25 | update: true 26 | before_install: 27 | - python --version 28 | - uname -a 29 | - lsb_release -a 30 | install: 31 | - pip install tox 32 | - virtualenv --version 33 | - pip --version 34 | - tox --version 35 | script: 36 | - tox -v 37 | after_failure: 38 | - more .tox/log/* | cat 39 | - more .tox/*/log/* | cat 40 | notifications: 41 | email: 42 | on_success: never 43 | on_failure: never 44 | -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | 2 | Authors 3 | ======= 4 | 5 | * David Seddon - https://seddonym.me 6 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | 1.0 (2022-10-4) 5 | --------------- 6 | 7 | * Remove upper bounds on dependencies. 8 | * Drop support for Python 3.6. 9 | * Add support for Python 3.9 and 3.10. 10 | 11 | 1.0b3 (2019-10-21) 12 | ------------------ 13 | 14 | * Upgrade to stable version of Grimp. 15 | 16 | 1.0b2 (2019-4-24) 17 | ----------------- 18 | 19 | * Upgraded Grimp. 20 | 21 | 1.0b1 (2019-2-9) 22 | ---------------- 23 | 24 | * Added documentation. 25 | 26 | 1.0a1 (2019-2-1) 27 | ----------------- 28 | 29 | * Initial release. 30 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Contributing 3 | ============ 4 | 5 | Contributions are welcome, and they are greatly appreciated! Every 6 | little bit helps, and credit will always be given. 7 | 8 | Bug reports 9 | =========== 10 | 11 | When `reporting a bug `_ please include: 12 | 13 | * Your operating system name and version. 14 | * Any details about your local setup that might be helpful in troubleshooting. 15 | * Detailed steps to reproduce the bug. 16 | 17 | Documentation improvements 18 | ========================== 19 | 20 | Impulse could always use more documentation, whether as part of the 21 | official docs, in docstrings, or even on the web in blog posts, 22 | articles, and such. 23 | 24 | Feature requests and feedback 25 | ============================= 26 | 27 | The best way to send feedback is to file an issue at https://github.com/seddonym/impulse/issues. 28 | 29 | If you are proposing a feature: 30 | 31 | * Explain in detail how it would work. 32 | * Keep the scope as narrow as possible, to make it easier to implement. 33 | * Remember that this is a volunteer-driven project, and that code contributions are welcome :) 34 | 35 | Development 36 | =========== 37 | 38 | To set up `impulse` for local development: 39 | 40 | 1. Fork `impulse `_ 41 | (look for the "Fork" button). 42 | 2. Clone your fork locally:: 43 | 44 | git clone git@github.com:your_name_here/impulse.git 45 | 46 | 3. Create a branch for local development:: 47 | 48 | git checkout -b name-of-your-bugfix-or-feature 49 | 50 | Now you can make your changes locally. 51 | 52 | 4. When you're done making changes, run all the checks, doc builder and spell checker with `tox `_ one command:: 53 | 54 | tox 55 | 56 | 5. Commit your changes and push your branch to GitHub:: 57 | 58 | git add . 59 | git commit -m "Your detailed description of your changes." 60 | git push origin name-of-your-bugfix-or-feature 61 | 62 | 6. Submit a pull request through the GitHub website. 63 | 64 | Pull Request Guidelines 65 | ----------------------- 66 | 67 | If you need some code review or feedback while you're developing the code just make the pull request. 68 | 69 | For merging, you should: 70 | 71 | 1. Include passing tests (run ``tox``) [1]_. 72 | 2. Update documentation when there's new API, functionality etc. 73 | 3. Add a note to ``CHANGELOG.rst`` about the changes. 74 | 4. Add yourself to ``AUTHORS.rst``. 75 | 76 | .. [1] If you don't have all the necessary python versions available locally you can rely on Travis - it will 77 | `run the tests `_ for each change you add in the pull request. 78 | 79 | It will be slower though ... 80 | 81 | Tips 82 | ---- 83 | 84 | To run a subset of tests:: 85 | 86 | tox -e envname -- pytest -k test_myfeature 87 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2019, David Seddon 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following 7 | conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following 10 | disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 16 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 19 | USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 20 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | Impulse 3 | ======= 4 | 5 | .. image:: https://img.shields.io/pypi/v/impulse.svg 6 | :target: https://pypi.org/project/impulse 7 | 8 | .. image:: https://img.shields.io/pypi/pyversions/impulse.svg 9 | :alt: Python versions 10 | :target: https://pypi.org/project/impulse/ 11 | 12 | .. image:: https://api.travis-ci.com/seddonym/impulse.svg?branch=master 13 | :target: https://app.travis-ci.com/github/seddonym/impulse 14 | 15 | * Free software: BSD license 16 | 17 | Impulse is a command line tool for exploring the imports in a Python package. 18 | 19 | It can be used to produce dependency graphs such as this: 20 | 21 | .. image:: https://raw.githubusercontent.com/seddonym/impulse/master/docs/_static/images/flask.png 22 | :align: center 23 | :alt: Graph of flask package. 24 | 25 | Installation 26 | ------------ 27 | 28 | Install Graphviz 29 | **************** 30 | 31 | First, you will need to install Graphviz. For more information, see the 32 | `Graphviz download page`_ or search for "how to install Graphviz" on your operating system. 33 | 34 | 35 | .. _Graphviz download page: https://www.graphviz.org/download/ 36 | 37 | Install Impulse 38 | *************** 39 | 40 | :: 41 | 42 | pip install impulse 43 | 44 | Install the Python package you wish to analyse 45 | ********************************************** 46 | 47 | :: 48 | 49 | pip install somepackage 50 | 51 | Command overview 52 | ---------------- 53 | 54 | There is currently only one command, feel free to suggest more by opening an issue or pull request. 55 | 56 | ``drawgraph`` 57 | ************* 58 | 59 | Draw a graph of the dependencies within any installed Python package or subpackage. 60 | 61 | The graph shows the relationship between all the immediate children of the package. An arrow indicates that there is 62 | at least one import by the child (or any of its descendants) from the subpackage where the arrow points. 63 | 64 | For example, in a graph of the package ``django.db``, there is an arrow from ``django.db.backends`` pointing to 65 | ``django.db.models``. This is because ``django.db.backends.base.schema`` imports ``django.db.models.Index``. All 66 | descendants are treated as belonging to the child they are in, so an import between any of those descendants will 67 | cause an import to be shown. 68 | 69 | **Command**:: 70 | 71 | impulse drawgraph django.db 72 | 73 | **Output:** 74 | 75 | .. image:: https://raw.githubusercontent.com/seddonym/impulse/master/docs/_static/images/django.db.png 76 | :align: center 77 | :alt: Graph of django.db package. -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = Impulse 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/_static/images/django.db.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seddonym/impulse/974e37fc9214b5f4e60eef8f5094d9cf68b363a6/docs/_static/images/django.db.png -------------------------------------------------------------------------------- /docs/_static/images/flask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seddonym/impulse/974e37fc9214b5f4e60eef8f5094d9cf68b363a6/docs/_static/images/flask.png -------------------------------------------------------------------------------- /docs/authors.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../AUTHORS.rst 2 | -------------------------------------------------------------------------------- /docs/changelog.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CHANGELOG.rst 2 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Configuration file for the Sphinx documentation builder. 4 | # 5 | # This file does only contain a selection of the most common options. For a 6 | # full list see the documentation: 7 | # http://www.sphinx-doc.org/en/stable/config 8 | 9 | # -- Path setup -------------------------------------------------------------- 10 | 11 | # If extensions (or modules to document with autodoc) are in another directory, 12 | # add these directories to sys.path here. If the directory is relative to the 13 | # documentation root, use os.path.abspath to make it absolute, like shown here. 14 | # 15 | import os 16 | # import sys 17 | # sys.path.insert(0, os.path.abspath('.')) 18 | 19 | 20 | # -- Project information ----------------------------------------------------- 21 | 22 | project = 'Impulse' 23 | copyright = '2019, David Seddon' 24 | author = 'David Seddon' 25 | 26 | # The short X.Y version 27 | version = '' 28 | # The full version, including alpha/beta/rc tags 29 | release = '1.0' 30 | 31 | 32 | # -- General configuration --------------------------------------------------- 33 | 34 | # If your documentation needs a minimal Sphinx version, state it here. 35 | # 36 | # needs_sphinx = '1.0' 37 | 38 | # Add any Sphinx extension module names here, as strings. They can be 39 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 40 | # ones. 41 | extensions = [ 42 | ] 43 | 44 | # Add any paths that contain templates here, relative to this directory. 45 | templates_path = ['_templates'] 46 | 47 | # The suffix(es) of source filenames. 48 | # You can specify multiple suffix as a list of string: 49 | # 50 | # source_suffix = ['.rst', '.md'] 51 | source_suffix = '.rst' 52 | 53 | # The master toctree document. 54 | master_doc = 'index' 55 | 56 | # The language for content autogenerated by Sphinx. Refer to documentation 57 | # for a list of supported languages. 58 | # 59 | # This is also used if you do content translation via gettext catalogs. 60 | # Usually you set "language" from the command line for these cases. 61 | language = None 62 | 63 | # List of patterns, relative to source directory, that match files and 64 | # directories to ignore when looking for source files. 65 | # This pattern also affects html_static_path and html_extra_path . 66 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 67 | 68 | # The name of the Pygments (syntax highlighting) style to use. 69 | pygments_style = 'sphinx' 70 | 71 | 72 | # -- Options for HTML output ------------------------------------------------- 73 | 74 | # The theme to use for HTML and HTML Help pages. See the documentation for 75 | # a list of builtin themes. 76 | # 77 | # on_rtd is whether we are on readthedocs.org 78 | on_rtd = os.environ.get('READTHEDOCS', None) == 'True' 79 | 80 | if not on_rtd: # only set the theme if we're building docs locally 81 | html_theme = 'sphinx_rtd_theme' 82 | 83 | # Theme options are theme-specific and customize the look and feel of a theme 84 | # further. For a list of options available for each theme, see the 85 | # documentation. 86 | # 87 | # html_theme_options = {} 88 | 89 | # Add any paths that contain custom static files (such as style sheets) here, 90 | # relative to this directory. They are copied after the builtin static files, 91 | # so a file named "default.css" will overwrite the builtin "default.css". 92 | html_static_path = ['_static'] 93 | 94 | # Custom sidebar templates, must be a dictionary that maps document names 95 | # to template names. 96 | # 97 | # The default sidebars (for documents that don't match any pattern) are 98 | # defined by theme itself. Builtin themes are using these templates by 99 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', 100 | # 'searchbox.html']``. 101 | # 102 | # html_sidebars = {} 103 | 104 | 105 | # -- Options for HTMLHelp output --------------------------------------------- 106 | 107 | # Output file base name for HTML help builder. 108 | htmlhelp_basename = 'impulse' 109 | 110 | 111 | # -- Options for LaTeX output ------------------------------------------------ 112 | 113 | latex_elements = { 114 | # The paper size ('letterpaper' or 'a4paper'). 115 | # 116 | # 'papersize': 'letterpaper', 117 | 118 | # The font size ('10pt', '11pt' or '12pt'). 119 | # 120 | # 'pointsize': '10pt', 121 | 122 | # Additional stuff for the LaTeX preamble. 123 | # 124 | # 'preamble': '', 125 | 126 | # Latex figure (float) alignment 127 | # 128 | # 'figure_align': 'htbp', 129 | } 130 | 131 | # Grouping the document tree into LaTeX files. List of tuples 132 | # (source start file, target name, title, 133 | # author, documentclass [howto, manual, or own class]). 134 | latex_documents = [ 135 | (master_doc, 'impulse.tex', 'Impulse Documentation', 136 | 'David Seddon', 'manual'), 137 | ] 138 | 139 | 140 | # -- Options for manual page output ------------------------------------------ 141 | 142 | # One entry per manual page. List of tuples 143 | # (source start file, name, description, authors, manual section). 144 | man_pages = [ 145 | (master_doc, 'impulse', 'Impulse Documentation', 146 | [author], 1) 147 | ] 148 | 149 | 150 | # -- Options for Texinfo output ---------------------------------------------- 151 | 152 | # Grouping the document tree into Texinfo files. List of tuples 153 | # (source start file, target name, title, author, 154 | # dir menu entry, description, category) 155 | texinfo_documents = [ 156 | (master_doc, 'impulse', 'Impulse Documentation', 157 | author, 'Impulse', "CLI for analyzing your Python project's imports.", 158 | 'Miscellaneous'), 159 | ] -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CONTRIBUTING.rst 2 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../README.rst 2 | 3 | .. toctree:: 4 | :hidden: 5 | :maxdepth: 2 6 | :caption: Contents: 7 | 8 | contributing 9 | authors 10 | changelog 11 | 12 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | set SPHINXPROJ=Impulse 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx>=1.3 2 | sphinx-rtd-theme 3 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = E731 3 | max-line-length = 100 4 | exclude = */migrations/*, tests/assets/* 5 | 6 | [mypy-pytest] 7 | ignore_missing_imports = True -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- encoding: utf-8 -*- 3 | import io 4 | from glob import glob 5 | from os.path import basename 6 | from os.path import dirname 7 | from os.path import join 8 | from os.path import splitext 9 | 10 | from setuptools import find_packages 11 | from setuptools import setup 12 | 13 | 14 | def read(*names, **kwargs): 15 | with io.open( 16 | join(dirname(__file__), *names), 17 | encoding=kwargs.get('encoding', 'utf8') 18 | ) as fh: 19 | return fh.read() 20 | 21 | 22 | setup( 23 | name='impulse', 24 | version='1.0', 25 | license='BSD 2-Clause License', 26 | description="Command line interface for analyzing Python imports.", 27 | long_description=read('README.rst'), 28 | long_description_content_type='text/x-rst', 29 | author='David Seddon', 30 | author_email='david@seddonym.me', 31 | project_urls={ 32 | 'Documentation': 'https://impulse-cli.readthedocs.io/', 33 | 'Source code': 'https://github.com/seddonym/impulse/', 34 | }, 35 | packages=find_packages('src'), 36 | package_dir={'': 'src'}, 37 | py_modules=[splitext(basename(path))[0] for path in glob('src/*.py')], 38 | include_package_data=True, 39 | zip_safe=False, 40 | classifiers=[ 41 | # complete classifier list: http://pypi.python.org/pypi?%3Aaction=list_classifiers 42 | 'Development Status :: 5 - Production/Stable', 43 | 'Intended Audience :: Developers', 44 | 'License :: OSI Approved :: BSD License', 45 | 'Operating System :: Unix', 46 | 'Operating System :: POSIX', 47 | 'Operating System :: Microsoft :: Windows', 48 | 'Programming Language :: Python', 49 | 'Programming Language :: Python :: 3.7', 50 | 'Programming Language :: Python :: 3.8', 51 | 'Programming Language :: Python :: 3.9', 52 | 'Programming Language :: Python :: 3.10', 53 | 'Programming Language :: Python :: Implementation :: CPython', 54 | 'Topic :: Utilities', 55 | ], 56 | install_requires=[ 57 | 'click>=6', 58 | 'graphviz>=0.10', 59 | 'grimp>=1', 60 | ], 61 | entry_points={ 62 | 'console_scripts': [ 63 | 'impulse = impulse.cli:main', 64 | ], 65 | }, 66 | ) 67 | -------------------------------------------------------------------------------- /src/impulse/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '1.0' 2 | -------------------------------------------------------------------------------- /src/impulse/application/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seddonym/impulse/974e37fc9214b5f4e60eef8f5094d9cf68b363a6/src/impulse/application/__init__.py -------------------------------------------------------------------------------- /src/impulse/application/use_cases.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | import tempfile 3 | import sys 4 | import os 5 | 6 | from graphviz import Digraph # type: ignore 7 | import grimp # type: ignore 8 | 9 | 10 | def draw_graph(module_name: str) -> None: 11 | """ 12 | Create a file showing a graph of the supplied package. 13 | Args: 14 | module_name: the package or subpackage name of any importable Python package. 15 | """ 16 | # Add current directory to the path, as this doesn't happen automatically. 17 | sys.path.insert(0, os.getcwd()) 18 | 19 | module = grimp.Module(module_name) 20 | graph = grimp.build_graph(module.package_name) 21 | module_children = graph.find_children(module.name) 22 | 23 | dot = Digraph( 24 | format='png', 25 | node_attr={'fontname': 'helvetica'} 26 | ) 27 | dot.attr( 28 | concentrate='true', # Merge lines together. 29 | ) 30 | for module_child in module_children: 31 | dot.node(module_child) 32 | 33 | # Dependencies between children. 34 | for upstream, downstream in itertools.permutations(module_children, r=2): 35 | if graph.direct_import_exists( 36 | imported=upstream, importer=downstream, as_packages=True): 37 | dot.edge(downstream, upstream) 38 | 39 | source_filename = tempfile.mkstemp()[1] 40 | dot.view(filename=source_filename, cleanup=True) 41 | -------------------------------------------------------------------------------- /src/impulse/cli.py: -------------------------------------------------------------------------------- 1 | import click 2 | from .application import use_cases 3 | 4 | 5 | @click.group() 6 | def main(): 7 | pass 8 | 9 | 10 | @main.command() 11 | @click.argument('module_name', type=str) 12 | def drawgraph(module_name): 13 | use_cases.draw_graph( 14 | module_name=module_name, 15 | ) 16 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seddonym/impulse/974e37fc9214b5f4e60eef8f5094d9cf68b363a6/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_placeholder.py: -------------------------------------------------------------------------------- 1 | def test_placeholder(): 2 | assert True 3 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | clean, 4 | check, 5 | docs, 6 | {py37,py38,py39,py310}, 7 | report 8 | 9 | [testenv] 10 | basepython = 11 | py37: {env:TOXPYTHON:python3.7} 12 | py38: {env:TOXPYTHON:python3.8} 13 | py39: {env:TOXPYTHON:python3.9} 14 | py310: {env:TOXPYTHON:python3.10} 15 | {clean,check,docs,report}: {env:TOXPYTHON:python3} 16 | setenv = 17 | PYTHONPATH={toxinidir}/tests 18 | PYTHONUNBUFFERED=yes 19 | passenv = 20 | * 21 | usedevelop = false 22 | deps = 23 | pytest==7.1.3 24 | pytest-travis-fold==1.3.0 25 | pytest-cov==4.0.0 26 | click==8.1.3 27 | commands = 28 | {posargs:pytest --cov --cov-report=term-missing -vv tests} 29 | impulse drawgraph grimp 30 | 31 | [testenv:check] 32 | deps = 33 | click==8.1.3 34 | flake8==5.0.4 35 | mypy==0.982 36 | skip_install = true 37 | commands = 38 | flake8 src tests setup.py 39 | mypy src/impulse tests 40 | 41 | [testenv:docs] 42 | deps = 43 | -r{toxinidir}/docs/requirements.txt 44 | commands = 45 | sphinx-build {posargs:-E} -b html docs dist/docs 46 | sphinx-build -b linkcheck docs dist/docs 47 | 48 | [testenv:report] 49 | deps = coverage==6.5.0 50 | skip_install = true 51 | commands = 52 | coverage report 53 | coverage html 54 | 55 | [testenv:clean] 56 | commands = coverage erase 57 | skip_install = true 58 | deps = coverage 59 | --------------------------------------------------------------------------------