GithubHelp home page GithubHelp logo

usort's Introduction


Safe, minimal import sorting for Python projects.

documentation version changelog license

μsort is a safe, minimal import sorter. Its primary goal is to make no "dangerous" changes to code. This is achieved by detecting distinct "blocks" of imports that are the most likely to be safely interchangeable, and only reordering imports within these blocks without altering formatting. Code style is left as an exercise for linters and formatters.

Within a block, µsort will follow common Python conventions for grouping imports based on source (standard library, third-party, first-party, or relative), and then sorting lexicographically within each group. This will commonly look like:

import re
from pathlib import Path
from typing import Iterable
from unittest.mock import call, Mock, patch

import aiohttp
from aiosqlite import connect

import foo
from bar import bar

from .main import main

Blocks are inferred from a number of real world conditions, including any intermediate statements between imports:

import warnings

import re
import sys

In this case, µsort detects two blocks–separated by the call to filterwarnings(), and will only sort imports inside of each block. Running µsort on this code will generate no changes, because each block is already sorted.

Imports can be excluded from blocks using the #usort:skip directive, or with #isort:skip for compatibility with existing codebases. µsort will leave these imports unchanged, and treat them as block separators.

See the User Guide for more details about how blocks are detected, and how sorting is performed.


µsort requires Python 3.6 or newer to run. Install µsort with:

$ pip install usort


To format one or more files or directories in-place:

$ usort format <path> [<path> ...]

To generate a diff of changes without modifying files:

$ usort diff <path>

To just validate that files are formatted correctly, like during CI:

$ usort check <path>


μsort is MIT licensed, as found in the LICENSE file.

usort's People


amyreese avatar bobronium avatar brandonthebuilder avatar dependabot[bot] avatar facebook-github-bot avatar fried avatar thatch avatar zsol avatar


 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar


 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

usort's Issues

Indent whitespace being added between imports

Reported in v0.6.4


# fmt: off
if __name__ == "__main__":
    import sys
    from foo import bar

After formatting it, ident whitespace is being added between the imports:

$ hg diff -U 10 | cat -e
diff --git a/ b/$
--- a/$
+++ b/$
@@ -1,6 +1,6 @@$
 # fmt: off$
 if __name__ == "__main__":$
     import sys$
+    $
     from foo import bar$

Consider: sorting collection literals

Not sure how feasible this would be, but it would be nice to have a # usort:keepsorted type of directive that would also sort collection literals, similar to IIRC what buildifier supports for bazel files. This would be really handy for keeping __all__ collections sorted in modules:

# usort: keepsorted
__all__ = [

I think it would be good enough to only support dict/list/set literals, and dictionaries would/should be sorted by key. Use the same lexicographical sorting used for modules.

Not sure if we can "sort" CST nodes in LibCST, so I think it's reasonable if we only sort collections using primitives like numbers or strings that are easy to reason about from the CST. Either ignore collections with unsortable elements entirely, or maybe just keep the unsortables in their original order, but after any number/str elements that we can sort. Sort numbers before strings if they're mixed, or vice-versa, just something consistent.

Open to bikeshedding any other details you can think of.

KeyError while sorting duplicate imports

On this file:

from a import x
from b import y
from b import y
from c import x

usort fails with

Traceback (most recent call last):
  File "usort/", line 80, in list_imports
    blocks = sorter.sortable_blocks(mod.body)
  File "usort/", line 192, in sortable_blocks
    current = self.split_inplace(current, overlap)
  File "usort/", line 169, in split_inplace
    new.imported_names[key] = block.imported_names.pop(key)
KeyError: 'y'

Support for --stdout and -

isort uses --stdout argument to write sorted imports to the stdout and also uses - argument to read from stdin.

usort doesn't support this behavior because of which I am unable to auto format in Emacs buffers.

isort behavior

$ isort --stdout -
import sys
import os
sys.stdout.write(os.getcwd() + "\n")
import os
import sys

sys.stdout.write(os.getcwd() + "\n")

usort behavior

$ usort --stdout -
Usage: usort [OPTIONS] COMMAND [ARGS]...
Try 'usort --help' for help.

Error: no such option: --stdout

Consider using mypyc to speed things up like black does

Black is significantly faster than ufsort, especially when it produces no changes.
I believe such performance difference may be due to black using mypyc:

black is 4.25 times faster on check:

curl -s -o

time black --check

# All done! ✨ 🍰 ✨
# 1 file would be left unchanged.
# black --check  0.08s user 0.02s system 75% cpu 0.137 total

time usort check            
# usort check  0.34s user 0.04s system 86% cpu 0.434 total

black is 2 times faster when making changes:

curl -s -o  
time black                                                                             
# reformatted

# All done! ✨ 🍰 ✨
# 1 file reformatted.
# black  0.15s user 0.02s system 75% cpu 0.229 total
time usort format                                                                      
# Sorted
# usort format  0.40s user 0.04s system 93% cpu 0.469 total

However looking at profiler run, I see that 3/5 of the time is taken by trailrunner (and nearly 90% of that time is spend in lock.acquire), despite usort was given only one file path:

pyinstrument --show-all -m usort format
pyinstrument --show-all -m usort format

_     ._   __/__   _ _  _  _ _/_   Recorded: 05:51:13  Samples:  167
/_//_/// /_\ / //_// / //_'/ //     Duration: 0.405     CPU time: 0.153
/   _/                      v4.1.1

Program: usort format

0.404 <module>  <string>:1
└─ 0.404 run_module
 ├─ 0.266 _run_module_code
 │  └─ 0.266 _run_code
 │     └─ 0.266 <module>  usort/
 │        ├─ 0.256 __call__  click/
 │        │  └─ 0.256 main  click/
 │        │     └─ 0.256 invoke  click/
 │        │        └─ 0.256 invoke  click/
 │        │           └─ 0.256 invoke  click/
 │        │              └─ 0.256 wrapper  usort/
 │        │                 └─ 0.256 format  usort/
 │        │                    └─ 0.255 usort_path  usort/
 │        │                       ├─ 0.242 run  trailrunner/
 │        │                       │  └─ 0.242 run  trailrunner/
 │        │                       │     ├─ 0.216 _chain_from_iterable_of_lists  concurrent/futures/
 │        │                       │     │  └─ 0.216 result_iterator  concurrent/futures/
 │        │                       │     │     └─ 0.216 result  concurrent/futures/
 │        │                       │     │        └─ 0.216 wait
 │        │                       │     │           └─ 0.216 lock.acquire  <built-in>:0
 │        │                       │     ├─ 0.019 __exit__  concurrent/futures/
 │        │                       │     │  └─ 0.019 shutdown  concurrent/futures/
 │        │                       │     │     └─ 0.019 join
 │        │                       │     │        └─ 0.019 _wait_for_tstate_lock
 │        │                       │     │           └─ 0.019 lock.acquire  <built-in>:0
 │        │                       │     └─ 0.004 _default_executor  trailrunner/
 │        │                       │        └─ 0.004 __init__  concurrent/futures/
 │        │                       └─ 0.012 walk  trailrunner/
 │        │                          └─ 0.012 walk  trailrunner/
 │        │                             └─ 0.012 gitignore  trailrunner/
 │        │                                └─ 0.011 pathspec  trailrunner/
 │        │                                   └─ 0.011 from_lines  pathspec/
 │        │                                      └─ 0.011 <listcomp>  pathspec/
 │        │                                         └─ 0.011 __init__  pathspec/
 │        │                                            └─ 0.009 compile
 │        │                                               └─ 0.009 _compile
 │        │                                                  └─ 0.008 compile
 │        │                                                     └─ 0.006 parse
 │        │                                                        └─ 0.006 _parse_sub
 │        │                                                           └─ 0.006 _parse
 │        └─ 0.010 <module>  usort/
 │           └─ 0.007 <module>  click/
 │              └─ 0.007 <module>  click/
 └─ 0.138 _get_module_details
    └─ 0.138 _get_module_details
       └─ 0.138 <module>  usort/
          └─ 0.138 <module>  usort/
             ├─ 0.114 <module>  usort/
             │  ├─ 0.101 <module>  libcst/
             │  │  ├─ 0.038 <module>  libcst/_nodes/
             │  │  │  ├─ 0.017 wrap
             │  │  │  │  └─ 0.017 _process_class
             │  │  │  │     ├─ 0.006 _frozen_get_del_attr
             │  │  │  │     │  └─ 0.006 _create_fn
             │  │  │  │     ├─ 0.004 _init_fn
             │  │  │  │     │  └─ 0.004 _create_fn
             │  │  │  │     └─ 0.004 _cmp_fn
             │  │  │  │        └─ 0.004 _create_fn
             │  │  │  └─ 0.016 <module>  libcst/_nodes/
             │  │  │     └─ 0.011 wrap
             │  │  │        └─ 0.010 _process_class
             │  │  ├─ 0.021 <module>  libcst/_nodes/
             │  │  │  └─ 0.020 <module>  libcst/_nodes/
             │  │  │     └─ 0.016 wrap
             │  │  │        └─ 0.016 _process_class
             │  │  │           ├─ 0.005 _init_fn
             │  │  │           │  └─ 0.004 _create_fn
             │  │  │           └─ 0.004 _cmp_fn
             │  │  │              └─ 0.004 _create_fn
             │  │  ├─ 0.014 <module>  libcst/_parser/
             │  │  │  └─ 0.010 <module>  libcst/_parser/
             │  │  │     └─ 0.008 <module>  libcst/_parser/conversions/
             │  │  │        └─ 0.006 <module>  libcst/_parser/types/
             │  │  │           └─ 0.006 wrap
             │  │  │              └─ 0.006 _process_class
             │  │  ├─ 0.008 <module>  libcst/metadata/
             │  │  ├─ 0.007 <module>  libcst/
             │  │  │  └─ 0.004 <module>  libcst/_parser/parso/pgen2/
             │  │  │     └─ 0.004 <module>  libcst/_parser/parso/pgen2/
             │  │  └─ 0.005 <module>  libcst/
             │  └─ 0.012 <module>  usort/
             │     └─ 0.012 <module>  usort/
             │        └─ 0.009 <module>  attr/
             │           └─ 0.005 <module>  attr/
             ├─ 0.016 <module>  trailrunner/
             │  └─ 0.016 <module>  trailrunner/
             │     ├─ 0.005 <module>  multiprocessing/
             │     │  └─ 0.005 <module>  multiprocessing/
             │     │     └─ 0.004 <module>  multiprocessing/
             │     └─ 0.004 <module>  concurrent/futures/
             └─ 0.005 <module>  usort/

So clearly, there's a lot of room for optimizations even before compiling code with mypyc :)

Examples comparing to isort's heuristics in the documentation seem outdated

I've tried the examples of isort breaking code that are given here and it doesn't seem like any of those are broken by isort:

Based on my testing, they haven't been broken since isort 5 (released July 2020) for sure so I'm a bit surprised to see those listed in the documentation that was added just 6 months ago. I can understand the benefit of usort being designed with safety as a priority but the way it's compared to isort is currently unfair.

isort doesn't really seem to advertise safety as a priority (though I'm pretty sure it's nonetheless considered important to it since isort 5) so that is still a good selling point (I find the mention of the strictly-parsed tree and how it causes bugs in isort to be particularly convincing), it would be good to not show code examples that isort doesn't actually break though :)

compatibility: handle isort:skip on the _first_ line of a wrapped statement

Something like

from x import (  # isort:skip

Currently is considered sortable (because it looks for the directive at the end of the statement, which here is after the closing paren. The fix here is probably intertwined with wrapping in #16, and some other libcst or fixit efforts that make edits to change imports.

Merge duplicate imports

usort accepts the snippet below as sorted.

from foo import bar
from foo import baz
from foo import baz, bar

while it can be shrank to

from foo import baz, bar

Would it be possible to perform this automatically, so I don't have to pay attention to the imports if new ones are added?

Usort from STDIN

Hi! Thanks for making this tool!
I think it would be useful to allow formatting of imports from a stream coming from another app, eg: I want to integrate this in Emacs and I would have to select some text and pass it to usort.
I suppose there are other ways that this can be useful.
Is it planned sometime?
Thank you!

What to do when future/stdlib/first_party categories aren't used?

One thing that I've considered a couple times is the use of the hardcoded category values for stdlib and first party modules. Specifically for cases where the user potentially does the following in their project config:

categories = ["catchall", "numpy"]
default_category = "catchall"

The future, stdlib, and first-party detection, all hardcode categories that wouldn't be in the configured category list, and I'm not sure how that ends up working in the sorting system, or what the correct way to deal with this would be (other than more configuration options that I don't think we want). Maybe we need to do something like known[module] = CAT_FIRST_PARTY if CAT_FIRST_PARTY in config.categories else config.default_category. But maybe something that makes that easier rather than repeating that everywhere? 🤷

AssertionError when formatting semicolon-delimited import lines

Originally noticed via omnilib/ufmt#115:

AssertionError is raised when formatting a file with multiple statements on a line that includes an import. The following example code will trigger this error in 1.0.4:

import sys; print(sys)
$ usort -V
usort, version 1.0.4

$ usort check
Error sorting
Would sort

As seen from the API:

Python 3.9.9 (main, Nov 28 2021, 12:38:04)
Type 'copyright', 'credits' or 'license' for more information
IPython 8.5.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import usort

In [2]: from pathlib import Path

In [3]: foo = Path("")

In [4]: content = b"import sys; print(sys)\n\n"

In [5]: config = usort.Config()

In [6]: result = usort.usort(content, config, foo)

In [7]: result.error
Out[7]: AssertionError()

In [8]: print(result.trace)
Traceback (most recent call last):
  File "/Users/amethyst/workspace/ufmt/.venv/lib/python3.9/site-packages/usort/", line 33, in usort
    new_mod = sorter.sort_module()
  File "/Users/amethyst/workspace/ufmt/.venv/lib/python3.9/site-packages/usort/", line 305, in sort_module
    new_module = self.wrapper.visit(self.transformer)
  File "/Users/amethyst/workspace/ufmt/.venv/lib/python3.9/site-packages/libcst/metadata/", line 204, in visit
    return self.module.visit(visitor)
  File "/Users/amethyst/workspace/ufmt/.venv/lib/python3.9/site-packages/libcst/_nodes/", line 90, in visit
    result = super(Module, self).visit(visitor)
  File "/Users/amethyst/workspace/ufmt/.venv/lib/python3.9/site-packages/libcst/_nodes/", line 237, in visit
    leave_result = visitor.on_leave(self, with_updated_children)
  File "/Users/amethyst/workspace/ufmt/.venv/lib/python3.9/site-packages/libcst/", line 71, in on_leave
    updated_node = leave_func(original_node, updated_node)
  File "/Users/amethyst/workspace/ufmt/.venv/lib/python3.9/site-packages/usort/", line 348, in leave_Module
    sorted_body = self.sorter.find_and_sort_blocks(
  File "/Users/amethyst/workspace/ufmt/.venv/lib/python3.9/site-packages/usort/", line 278, in find_and_sort_blocks
    blocks = list(self.sortable_blocks(body))
  File "/Users/amethyst/workspace/ufmt/.venv/lib/python3.9/site-packages/usort/", line 180, in sortable_blocks
    imp = import_from_node(stmt, self.config)
  File "/Users/amethyst/workspace/ufmt/.venv/lib/python3.9/site-packages/usort/", line 169, in import_from_node
    comments = import_comments_from_node(node)
  File "/Users/amethyst/workspace/ufmt/.venv/lib/python3.9/site-packages/usort/", line 39, in import_comments_from_node
    assert len(node.body) == 1

Support for Python 3.10's match operator

Currently the match operator is not supported because of the version of libcst that is in use. #180
0.4.1 only has support for Python up to 3.8, while 0.4.2 (and up) has support for every other Python version.
Is it likely to be fixed in the near future?

Should have more configurable sections

This will probably require some decisions about whether the special cases in sorting from isort should be preserved, such that this is still a no-op on already-isorted code so you can switch back and forth.

Unstable sorting with mixed category imports

When basic imports include modules from multiple categories, sorting can be unstable because the sort_key isn't updated when changing order of the import names:

Test case:

    def test_sorting_mixed_category_imports(self) -> None:
                import os, attr
                import difflib
                import third_party
                import difflib

                import attr, os
                import third_party

Produces the following test failure:

(.venv) [email protected] ~/workspace/usort mixed± » make format test lint
python -m ufmt format usort
python -m coverage run -m usort.tests
FAIL: test_sorting_mixed_category_imports (usort.tests.functional.UsortStringFunctionalTest)
Traceback (most recent call last):
  File "/Users/jreese/workspace/usort/usort/tests/", line 632, in test_sorting_mixed_category_imports
  File "/Users/jreese/workspace/usort/usort/tests/", line 58, in assertUsortResult
AssertionError: µsort result was not stable on second pass:


import os, attr
import difflib
import third_party

First Pass:

import attr, os
import difflib

import third_party

Second Pass:

import difflib

import attr, os

import third_party

Ran 85 tests in 4.119s

FAILED (failures=1)
make: *** [test] Error 1

Handle encoding correctly

Right now read_text uses the system encoding only. We should read bytes and allow libcst to decode/encode.

General directive customization

Some directives are standalone, e.g.

# fmt: off

Others come before the line (or statement) they affect

# type LEADING
# pyre-ignore: ...
import x

While others must happen at end of line

from x import (  # isort:skip
  # type TRAILING
  z,  # noqa: F123
)  # hypothetical TRAILING_LAST_LINE

These basic types can be detected by prefix match and handled when ordering. Right now leading comments (in a block) are special-cased in partition_leading_lines but ones that are of the LEADING type need to be excepted from that and kept with the statement.

These should be configurable, but with defaults for common tools.

Black enforces a blank line before comments

If a and b are in the same category, we want to remove the blank line before the comment, but black wants to put it back.

import a

# comment
import b

We should just fixup_whitespace to include this so that check can be used independently.

Normalize leading blank lines when moving imports

Running µsort on this sort of input does what I would consider to be the "wrong thing":

import re

import usort
import trailrunner


This gets sorted into:

import re

import trailrunner

import usort


Ie, the leading blank line above import usort is kept when moving usort below trailrunner, even though they are in the same category.

Respect names case when sorting (CONSTANT, Class, function/variable_name)


usort diff (no config) produces this diff:

-from .names import ABBR_TO_FULL_NAME, FULL_NAME_TO_ABBR, Path, deminify, minify
+from .names import ABBR_TO_FULL_NAME, deminify, FULL_NAME_TO_ABBR, minify, Path

However, I find the way it was ordered already is more consistent and "right".
isort respects that order and doesn't change it by default.


Add option to preserve CONSTANT, Class, function/variable_name order when sorting.

cProfile isn't considered stdlib

I suspect this was broken with the case-related changes in #19 and reusing SortableImport.first_module for sorting as well as section detection.

This is properly sorted and formatted:

import cProfile
import os

but current (0.5.0a3) insists on putting cProfile last, outside the stdlib section.

$ usort list-imports --debug 1 blocks:
    1 SortableImport(sort_key=SortKey(category_index=2, is_from_import=False, ndots=0), first_module='cprofile', first_dotted_import='cprofile', imported_names={'cProfile': 'cProfile'}) (third_party)
    0 SortableImport(sort_key=SortKey(category_index=1, is_from_import=False, ndots=0), first_module='os', first_dotted_import='os', imported_names={'os': 'os'}) (standard_library)

Feature: Add command line flag to specify location of usort config

By default, µsort will automatically look for the "nearest" pyproject.toml for each file being formatted, and load the config from that location before formatting the associated file. For some projects, it would be preferable to specify the location of a canonical pyproject.toml that will be used as configuration for all files being formatted, regardless of any other pyproject.toml found near these files. This would also enable loading configs from a generic toml file, but we should always expect to find config values in the PEP-518 compliant tool.usort table.

The flag should probably be something like:

$ usort --config path/to/config.toml format ...

Normally, usort.sorting.usort_path() and usort.sorting.usort_stdin() call Config.find(...) to locate pyproject.toml and receive an appropriate Config object. A solution for this should probably set a flag or call a classmethod on Config to override that behavior and always supply the preferred configuration.

Support excluding files from a configuration file

As of now usort does not provide an option to exclude certain files from a configuration file. There is the # usort:skip (or # isort:skip) directive, but this gets tedious fast if one wants to skip a complete directory or files based on a pattern.

There are several options from other formatters / linters how to achieve this. Since usort should be used with a code formatter and black is an obvious choice, IMO it would be a good idea to adhere to their --exclude behavior, i.e. regular expression matching.

Add pre-commit hook

Is there any interest in adding a .pre-commit-hooks.yaml file so that usort can be easily used with pre-commit? I'd be willing to put this together.

Sometimes requires two passes with duplicated names

A source file like

from a import A
from d import D
from e import E

from z import A
from b import B
from c import C

the first pass moves Z to the end, the second pass moves b and c to their correct location. This has everything to do with detecting a new block starting with z, but instead of freezing its location, allowing it to be moved (only down).

Should combine imports (in reasonable cases)

For imports that don't have inline comments, we can combine and reorder their names. This is a common request, and although I don't want to do much in the way of formatting, we can support the common case pretty easily.

from os import signal
from os import path


from os import path, signal

Sorting names within a single statement is a simple case of this as well.

Feature: a new subcommand called "explain"

Right now list-imports --debug tells you a lot of information, but what most people are probably interested in is why blocks get split. This subcommand should tell you a human-readable version of why not is_sortable_import on the one that causes a new block to be created, in addition to a simplified version of the category for imports and maybe the sort_key.

Add side_effect_modules to config

Some imports have known side-effects, and should be consdiered as block separators. Would be nice if these could be set in configuration. I don't think there are any in stdlib, but django.settings as an example may import your settings module that changes sys.path.

Misclassifies local imports as third party

Clone aql and sort it:

(.venv) [email protected] ~/workspace/aql main  » python -m usort format --diff aql
--- a/aql/tests/engines/
+++ b/aql/tests/engines/
@@ -2,8 +2,8 @@
 # Licensed under the MIT license

 from .base import EngineTest
+from .integration import IntegrationTest  # isort:skip                                                                                                                               from .mysql import MysqlEngineTest
 from .sql import SqlEngineTest
 from .sqlite import SqliteEngineTest
-from .integration import IntegrationTest  # isort:skip
--- a/aql/tests/engines/
+++ b/aql/tests/engines/
@@ -3,9 +3,9 @@

 from sqlite3 import OperationalError

+import aql
 from aiounittest import AsyncTestCase
-import aql


This results in the local import aql appearing above the third-party from aiounittest import ....

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.