- About
- Installation
- Configuration
- Labels
- Test Frameworks
- OpenAPI documentation
- Remote Recording
- Context manager
- Development
appmap-python is a Python package for recording AppMaps of your code. "AppMap" is a data format which records code structure (modules, classes, and methods), code execution events (function calls and returns), and code metadata (repo name, repo URL, commit SHA, labels, etc). It's more granular than a performance profile, but it's less granular than a full debug trace. It's designed to be optimal for understanding the design intent and structure of code and key data flows.
There are several ways to record AppMaps of your Python program using the appmap package:
Run your tests (pytest, unittest) with the environment variable
APPMAP=true. An AppMap will be generated for each test case.Use the
appmap.recordcontext manager to control recording. The context manager takes an instance of anappmap.Recording, which can be used to generate the AppMap.Run your application server with AppMap remote recording enabled, and use the AppLand browser extension to start, stop, and upload recordings.
Once you have made a recording, there are two ways to view automatically generated diagrams of the AppMaps.
The first option is to load the diagrams directly in your IDE, using the AppMap extension for VSCode.
The second option is to upload them to the AppLand server using the AppLand CLI.
- Python >=3.6
- Pytest >=6.1.2
Support for new versions is added frequently, please check back regularly for updates.
If your project uses pip for dependency management, add the appmap package to the requirements file or install it directly with
pip install appmapFor projects that use poetry , add the appmap package to pyproject.toml.
Add your modules as path entries in appmap.yml, and external packages (distributions) as dist:
name: my_python_apppackages: - path: app.mod1shallow: true - path: app.mod2exclude: - MyClass - MyOtherClass.my_instance_method - MyOtherClass.my_class_method - dist: Djangoexclude: - django.dbNote that excludes are resolved relative to the associated path. So, for example, this configuration excludes app.mod2.MyClass.
For external distribution packages use the dist specifier; the names are looked up in the database of installed Python distributions. This is generally the same package name as you'd give to pip install or put in pyproject.toml. You can additionally use path and exclude on dist entries to limit the capture to specific patterns.
Note by default shallow capture is enabled on dist packages, supressing tracking of most internal execution flow, which allows you to capture the interaction without getting bogged down with detail. If this isn't what you want, use shallow: false. You can also use shallow: true on path entries.
APPMAPiftrue, code will be instrumented and AppMaps will be generated. Not case-sensitive, defaults to 'false'.APPMAP_CONFIGspecifies the configuration file to use. Defaults toappmap.ymlin the current directoryAPPMAP_LOG_LEVELspecifies log level to use, from the setCRITICAL,ERROR,WARNING,INFO,DEBUG. Not case-sensitive, defaults toWARNING.APPMAP_OUTPUT_DIRspecifies the root directory for writing AppMaps. Defaults totmp/appmap.APPMAP_DISPLAY_PARAMSenables rendering of parameters as strings. Iftrue(the default, not case-sensitive), parameters are rendered usingrepr. Iffalse, a generic string is used instead.
The AppMap data format provides for class and function labels, which can be used to enhance the AppMap visualizations, and to programatically analyze the data.
You can apply function labels using the appmap.labels decorator in your Python code. To apply a labels to a function, decorate the function with @appmap.labels.
For example
importappmap.labelsclassApiKey@appmap.labels('provider.authentication', 'security')defauthenticate(self, key): # logic to verify the key here...Then the AppMap metadata section for this function will include:
{"name": "authenticate", "type": "function", "labels": [ "provider.authentication", "security" ] }appmap-python supports recording pytest and unittest test cases.
appmap-python is a pytest plugin. When it's installed in a project that uses pytest, it will be available to generate AppMaps.
root@e9987eaa93c8:/src/appmap/test/data/pytest# pip show appmap Name: appmap Version: 0.0.0 Summary: Create AppMap files by recording a Python application. Home-page: None Author: Alan Potter Author-email: [email protected] License: None Location: /usr/local/lib/python3.9/site-packages Requires: orjson, PyYAML, inflection Required-by: root@e9987eaa93c8:/src/appmap/test/data/pytest# APPMAP=true APPMAP_LOG_LEVEL=info pytest -svv [2021-02-10 11:37:59,345] INFO root: appmap enabled: True [2021-02-10 11:37:59,350] INFO appmap._implementation.configuration: ConfigFilter, includes{'simple'} [2021-02-10 11:37:59,350] INFO appmap._implementation.configuration: ConfigFilter, excludes set() ===================================================================== test session starts ===================================================================== platform linux -- Python 3.9.1, pytest-6.2.2, py-1.10.0, pluggy-0.13.1 -- /usr/local/bin/python cachedir: .pytest_cache rootdir: /src, configfile: pytest.ini plugins: appmap-0.0.0 collected 1 item test_simple.py::test_hello_world [2021-02-10 11:37:59,482] INFO appmap.pytest: starting recording /tmp/pytest/test_hello_world.appmap.json [2021-02-10 11:37:59,484] INFO appmap._implementation.configuration: included class simple.Simple [2021-02-10 11:37:59,484] INFO appmap._implementation.configuration: included functionsimple.Simple.hello [2021-02-10 11:37:59,489] INFO appmap._implementation.configuration: included functionsimple.Simple.hello_world [2021-02-10 11:37:59,490] INFO appmap._implementation.configuration: included functionsimple.Simple.world [2021-02-10 11:37:59,828] INFO appmap.pytest: wrote recording /tmp/pytest/test_hello_world.appmap.json PASSED ====================================================================== 1 passed in 0.45s ======================================================================import appmap.unittest. Instruments subclasses of unittest.TestCase and records each test_* function in the subclasses. You can also use python -m appmap.unittest exactly like python -m unittest and leave your code unmodified (just remember to set the APPMAP=true environment variable).
Once you've configured your tests to generate AppMaps, run the tests with the APPMAP=true in the environment. For example, to run a pytest test suite:
$ APPMAP=true pytestAfter you run the test suite of your flask or Django web application, appmap-swagger can be used to automatically generate OpenAPI documentation for all routes covered by the tests. Please refer to that project for usage instructions.
appmap-python supports remote recording of Django and Flask web applications. Import the appropriate remote recording support into your web app.
Add appmap.django.Middleware to your MIDDLEWARE.
For projects that use a Flask application factory, installing appmap-python automatically configures the project for remote recording. No further modifications are required. When the application initializes, appmap-python adds middleware that handles the /_appmap/record routes.
For projects that don't provide an application factory, appmap-python can be used as a Flask extension.
For example:
fromflaskimportFlaskfromappmap.flaskimportAppmapFlaskapp=Flask(__name__) appmap_flask=AppmapFlask(app)This will add the /_appmap/record routes your app.
Once you've configured your web app to add the remote-recording routes, you can use the routes to manage recordings. The AppLand browser extension, CLI, or just plain cURL will all work for this.
As when running tests, start the web server with APPMAP=true in the environment. For example, to start a Flask app:
$ APPMAP=true flask runAn app with remote recording enabled supports these routes:
POST /_appmap/recordStarts a new recording200 if the recording was started successfully 409 if there's already a recording in progress
GET /_appmap/recordReturns JSON describing current recording state 200 with body{"enabled": true }enabledindicates whether recording has been enabledDELETE /_appmap/recordReturns AppMap as JSON 200 with AppMap as body 404 if there's no recording in progress
You can use appmap.record as a context manager to record your code.
With a file called record_sample.py like this
importosimportsysimportappmapr=appmap.Recording() withr: importsampleprint(sample.C().hello_world(), file=sys.stderr) withos.fdopen(sys.stdout.fileno(), "wb", closefd=False) asstdout: stdout.write(appmap.generation.dump(r)) stdout.flush()and a source file called sample.py like this
classC: defmake_str(self, s): returns; defhello_world(self): returnf'{self.make_str("Hello")}{self.make_str("world!")}'as well as an appmap.yml
name: samplepackages: - path: sampleyou can generate a recording of the code
% APPMAP=true python record_sample.py > record_sample.appmap.json % jq '.events | length' record_sample.appmap.json 6 % jq < record_sample.appmap.json | head -10{"version": "1.4", "metadata":{"language":{"name": "python", "engine": "CPython", "version": "3.9.1" }, "client":{"name": "appmap",Clone the repo to begin development. Note that vendored dependencies are included as submodules.
% g clone --recurse-submodules https://github.com/applandinc/appmap-python.git Cloning into 'appmap-python'... remote: Enumerating objects: 167, done. remote: Counting objects: 100% (167/167), done. remote: Compressing objects: 100% (100/100), done. remote: Total 962 (delta 95), reused 116 (delta 61), pack-reused 795 Receiving objects: 100% (962/962), 217.31 KiB | 4.62 MiB/s, done. Resolving deltas: 100% (653/653), done. Submodule 'extern/wrapt' (https://github.com/applandinc/wrapt.git) registered for path 'vendor/wrapt' Cloning into '/private/tmp/appmap-python/vendor/wrapt'... remote: Enumerating objects: 46, done. remote: Counting objects: 100% (46/46), done. remote: Compressing objects: 100% (39/39), done. remote: Total 2537 (delta 9), reused 19 (delta 4), pack-reused 2491 Receiving objects: 100% (2537/2537), 755.94 KiB | 7.48 MiB/s, done. Resolving deltas: 100% (1643/1643), done. Submodule path 'vendor/wrapt': checked out '9bdfbe54b88a64069cba1f3c36e77edc3c1339c9' % ls appmap-python/vendor/wrapt LICENSE Makefile appveyor.yml docs src tests MANIFEST.in README.rst blog setup.py tddium.yml tox.iniAs a package intended to be installed in as many environments as possible, appmap-python needs to avoid using features of Python or the standard library that were added after the oldest version currently supported (see above).
poetry for dependency management:
% brew install poetry % cd appmap-python % poetry install --extras test pylint for linting:
% cd appmap-python % poetry run pylint appmap -------------------------------------------------------------------- Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00) [Note that the current configuration requires a 10.0 for the Travis build to pass. To make this easier to achieve, convention and refactoring checks have both been disabled. They should be reenabled as soon as possible.]
pytest for testing:
% cd appmap-python % poetry run pytest Additionally, the tox configuration provides the ability to run the tests for all supported versions of Python and djanggo.
tox requires that all the correct versions of Python to be available to create the test environments. pyenv is an easy way to manage multiple versions of Python, and the xxenv-latest plugin can help get all the latest versions.
% brew install pyenv % git clone https://github.com/momo-lab/xxenv-latest.git "$(pyenv root)"/plugins/xxenv-latest % cd appmap-python % pyenv latest local 3.{9,6,7,8} % forvin 3.{9,6,7,8};do pyenv latest install $v;done % poetry run toxcoverage for coverage:
% cd appmap-python % poetry run coverage run -m pytest % poetry run coverage html % open htmlcov/index.html