Usage ===== Specifying Files And Directories To Convert ------------------------------------------- To convert a single file: .. code-block:: console $ cat myscript.py print(hello := 'world') $ walrus myscript.py Now converting: '/path/to/project/myscript.py' $ cat myscript.py # file overwritten with conversion result if False: hello = NotImplemented def _walrus_wrapper_hello_5adbf5ee911449cba75e35b9ef97ea80(expr): """Wrapper function for assignment expression.""" global hello hello = expr return hello print(_walrus_wrapper_hello_5adbf5ee911449cba75e35b9ef97ea80('world')) To convert the whole project at the current working directory (overwrites all Python source files inside): .. code-block:: console $ walrus . Multiple files and directories may be supplied at the same time: .. code-block:: console $ walrus script_without_py_extension /path/to/another/project .. note:: When converting a directory, ``walrus`` will recursively find all the Python source files in the directory (and its subdirectories, if any). Whether a file is a Python source file is determined by its file extension (``.py`` or ``.pyw``). If you want to convert a file without a Python extension, you will need to explicitly specify it in the argument list. If you prefer a side-effect free behavior (do not overwrite files), you can use the **simple mode**. Simple mode with no arguments (read from stdin, write to stdout): .. code-block:: console $ printf 'print(hello := "world")\n' | python3 walrus.py -s if False: hello = NotImplemented def _walrus_wrapper_hello_fbf3a9dabd2b40348815e3f2b22a1683(expr): """Wrapper function for assignment expression.""" global hello hello = expr return hello print(_walrus_wrapper_hello_fbf3a9dabd2b40348815e3f2b22a1683("world")) Simple mode with a file name argument (read from file, write to stdout): .. code-block:: console $ cat myscript.py print(hello := 'world') $ walrus -s myscript.py if False: hello = NotImplemented def _walrus_wrapper_hello_d1e6c2a11a76400aa9745bd90b3fb52a(expr): """Wrapper function for assignment expression.""" global hello hello = expr return hello print(_walrus_wrapper_hello_d1e6c2a11a76400aa9745bd90b3fb52a('world')) $ cat myscript.py print(hello := 'world') In simple mode, no file names shall be provided via positional arguments. Archiving And Recovering Files ------------------------------ If you are not using the simple mode, ``walrus`` overwrites Python source files, which could potentially cause data loss. Therefore, a built-in archiving functionality is enabled by default. The original copies of the Python source files to be converted will be packed into an archive file and placed under the ``archive`` subdirectory of the current working directory. To opt out of archiving, use the CLI option ``-na`` (``--no-archive``), or set environment variable ``WALRUS_DO_ARCHIVE=0``. To use an alternative name for the archive directory (other than ``archive``), use the CLI option ``-k`` (``--archive-path``), or set the environment variable ``WALRUS_ARCHIVE_PATH``. To recover files from an archive file, use the CLI option ``-r`` (``--recover``): .. code-block:: console $ walrus -r archive/archive-20200814222751-f3a514d40d69c6d5.tar.gz Recovered files from archive: 'archive/archive-20200814222751-f3a514d40d69c6d5.tar.gz' $ ls archive/ archive-20200814222751-f3a514d40d69c6d5.tar.gz By default, the archive file is still retained after recovering from it. If you would like it to be removed after recovery, specify the CLI option ``-r2``: .. code-block:: console $ walrus -r archive/archive-20200814222751-f3a514d40d69c6d5.tar.gz -r2 Recovered files from archive: 'archive/archive-20200814222751-f3a514d40d69c6d5.tar.gz' $ ls archive/ If you also want to remove the archive directory if it becomes empty, specify the CLI option ``-r3``: .. code-block:: console $ walrus -r archive/archive-20200814222751-f3a514d40d69c6d5.tar.gz -r3 Recovered files from archive: 'archive/archive-20200814222751-f3a514d40d69c6d5.tar.gz' $ ls archive/ ls: cannot access 'archive/': No such file or directory .. warning:: To improve stability of file recovery, the archive file records the original absolute paths of the Python source files. Thus, file recovery is only guaranteed to work correctly on **the same machine** where the archive file was created. Never perform the recovery operation on an arbitrary untrusted archive file. Doing so may allow attackers to overwrite any files in the system. Conversion Options ------------------ By default, ``walrus`` automatically detects file line endings and use the same line ending for code insertion. If you want to manually specify the line ending to be used, use the CLI option ``-l`` (``--linesep``) or the ``WALRUS_LINESEP`` environment variable. By default, ``walrus`` automatically detects file indentations and use the same indentation for code insertion. If you want to manually specify the indentation to be used, use the CLI option ``-t`` (``--indentation``) or the ``WALRUS_INDENTATION`` environment variable. By default, ``walrus`` parse Python source files as the latest version. If you want to manually specify a version for parsing, use the CLI option ``-vs`` (``-vf``, ``--source-version``, ``--from-version``) or the ``WALRUS_SOURCE_VERSION`` environment variable. By default, code insertion of ``walrus`` conforms to :pep:`8`. To opt out and get a more compact result, specify the CLI option ``-n8`` (``--no-pep8``) or set environment variable ``WALRUS_PEP8=0``. Runtime Options --------------- Specify the CLI option ``-q`` (``--quiet``) or set environment variable ``WALRUS_QUIET=1`` to run in quiet mode. Specify the CLI option ``-C`` (``--concurrency``) or set environment variable ``WALRUS_CONCURRENCY`` to specify the number of concurrent worker processes for conversion. Use the ``--dry-run`` CLI option to list the files to be converted without actually performing conversion and archiving. By running ``walrus --help``, you can see the current values of all the options, based on their default values and your environment variables. API Usage --------- If you want to programmatically invoke ``walrus``, you may want to look at :doc:`api`. The :func:`walrus.convert` and :func:`walrus.walrus` functions should be most commonly used. Distutils/Setuptools Integration ------------------------------- ``walrus`` can also be directly integrated within your ``setup.py`` script to dynamically convert *assignment expressions* upon installation: .. code-block:: python :emphasize-lines: 21,33,36 import subprocess import sys try: from setuptools import setup from setuptools.command.build_py import build_py except ImportError: from distutils.core import setup from distutils.command.build_py import build_py version_info = sys.version_info[:2] class build(build_py): """Add on-build backport code conversion.""" def run(self): if version_info < (3, 8): try: subprocess.check_call( # nosec [sys.executable, '-m', 'walrus', '--no-archive', 'PACKAGENAME'] ) except subprocess.CalledProcessError as error: print('Failed to perform assignment expression backport compiling.' 'Please consider manually install `bpc-walrus` and try again.', file=sys.stderr) sys.exit(error.returncode) build_py.run(self) setup( ... setup_requires=[ 'bpc-walrus; python_version < "3.8"', ], cmdclass={ 'build_py': build, }, ) Or, as :pep:`518` proposed, you may simply add ``bpc-walrus`` to the ``requires`` list from the ``[build-system]`` section in the ``pyproject.toml`` file: .. code-block:: toml :emphasize-lines: 3 [build-system] # Minimum requirements for the build system to execute. requires = ["setuptools", "wheel", "bpc-walrus"] # PEP 508 specifications. ...