Wxc

wxc is a command line tool to query source code from the environment. wxc is said as which. wxc serves similar purpose as man pages but for python source code.

Example usages

$poetry run wxc django.db.models.Q
/Users/user/Library/Caches/pypoetry/virtualenvs/code-census-Ypc4NYco-py3.9/lib
/python3.9/site-packages/django/db/models/query_utils.py:57
$poetry run wxc django.db.models.Q --help
usage: wxc [-h] [-v | -f] [-s] [--lines] name

positional arguments:
  name           target Python scope (package.module.submodule.class.method)

optional arguments:
  -h, --help     show this help message and exit
  -v, --version  print module version
  -f, --full     print a full report
  -s, --source   print the source code
  --lines        show source lines

Get the report of the name by passing the -f flag.

$prun wxc django.db.models.Q -f
source =
/Users/user/Library/Caches/pypoetry/virtualenvs/code-census-Ypc4NYco-py3.9/lib
/python3.9/site-packages/django/db/models/query_utils.py:57
in_stdlib = False
name = django.db.models.Q
version = 3.2.7

Display the source code

$poetry run wxc django.db.models.Q --source
class Q(tree.Node):
    """
    Encapsulate filters as objects that can then be combined logically (using
    `&` and `|`).
    """
    # Connection types
    AND = 'AND'
    OR = 'OR'
    default = AND
    conditional = True

    def __init__(self, *args, _connector=None, _negated=False, **kwargs):
        super().__init__(children=[*args, *sorted(kwargs.items())], 
        connector=_connector, negated=_negated)

    def _combine(self, other, conn):
        if not(isinstance(other, Q) or getattr(other, 'conditional', False) is True):
            raise TypeError(other)

        if not self:
            return other.copy() if hasattr(other, 'copy') else copy.copy(other)
        elif isinstance(other, Q) and not other:
            _, args, kwargs = self.deconstruct()
            return type(self)(*args, **kwargs)

        obj = type(self)()
        obj.connector = conn
        obj.add(self, conn)
        obj.add(other, conn)
        return obj

    def __or__(self, other):
        return self._combine(other, self.OR)

    def __and__(self, other):
        return self._combine(other, self.AND)

    def __invert__(self):
        obj = type(self)()
        obj.add(self, self.AND)
        obj.negate()
        return obj

    def resolve_expression(self, query=None, allow_joins=True, reuse=None, 
    		summarize=False, for_save=False):
        # We must promote any new joins to left outer joins so that when Q is
        # used as an expression, rows aren't filtered due to joins.
        clause, joins = query._add_q(
            self, reuse, allow_joins=allow_joins, split_subq=False,
            check_filterable=False,
        )
        query.promote_joins(joins)
        return clause

    def deconstruct(self):
        path = '%s.%s' % (self.__class__.__module__, self.__class__.__name__)
        if path.startswith('django.db.models.query_utils'):
            path = path.replace('django.db.models.query_utils', 
            										'django.db.models')
        args = tuple(self.children)
        kwargs = {}
        if self.connector != self.default:
            kwargs['_connector'] = self.connector
        if self.negated:
            kwargs['_negated'] = True
        return path, args, kwargs

/Users/user/Library/Caches/pypoetry/virtualenvs/code-census-Ypc4NYco-py3.9/lib
/python3.9/site-packages/django/db/models/query_utils.py:57

wxc works for the non-installed Python code as well. Here is example from the cli.py

$tail code_census/cli.py
def create_db(db_url: str):
    engine = create_engine(db_url, echo=True)
    cfg = Config("alembic.ini")
    with engine.begin() as connection:
        cfg.attributes["connection"] = connection
        command.upgrade(cfg, "head", sql=True)


if __name__ == "__main__":
    cli()
    
$poetry run wxc code_census.cli.create_db --source
@click.option(
    "--db-url", type=str, required=True, envvar="DB_URL", help=HELP_TEXT["db_url"]
)
def create_db(db_url: str):
    engine = create_engine(db_url, echo=True)
    cfg = Config("alembic.ini")
    with engine.begin() as connection:
        cfg.attributes["connection"] = connection
        command.upgrade(cfg, "head", sql=True)

/Users/user/code/personal/code_census/code_census/cli.py:197

When wxc doesn’t work?

  • Django project in a repository. Django expects to load the settings and app configs to load first. Hence, it doesn’t work.

    $PYTHONPATH="." poetry run wxc customer.service.get_active_customers
    ...
      File "/Users/user/Library/Caches/pypoetry/virtualenvs/code-census-Ypc4NYco-py3.9/lib/python3.9/site-packages/django/apps/registry.py", line 136, in check_apps_ready
        raise AppRegistryNotReady("Apps aren't loaded yet.")
    django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.
    
  • Compiled python source code and builtin object.

    $ poetry run wxc print --source
    
    ERROR failed to locate source data. 'print' is a builtin object.
    
    $poetry run wxc os.listdir --source
    
    ERROR failed to locate source data. 'os.listdir' is a C-compiled function.
    

References