Original: BRETT CANNON

The cat under the pea flower

English: snarky. Ca/the things – wa…

For our VS Code Python plug-in [1], I wrote a simple script to generate a change log [2] (similar to Towncrier [3] but simpler, Markdown supported, which met our needs). One step in the publishing process is to run Python News, which points Python to the “news” directory in our code.

The other day, a collaborator asked how this works, and it seems like everyone on our team knows how to use -m? (See my article on using PIP with -m [4] to see why.)

This made me realize that other people might not know that there are various ways to point Python to code to execute, hence this article.

1. Through standard input and pipes

Because how you pipe things to a process is part of the shell, I’m not going to go into that. Needless to say, you can pass code into Python.

#Pipes pass content to Python
echo "print('hi')" | python
Copy the code

This obviously works as well if you redirect files to Python.

#Redirect a file to Python
python < spam.py
Copy the code

None of this is too surprising, thanks to Python’s UNIX heritage.

2, through the-cSpecified string

If you just need to check something quickly, you can pass the code as a string on the command line.

# Use python's -c argument
python -c "print('hi')"
Copy the code

I personally use it when I need to check just one or two lines of code, rather than start the REPL. The -c argument can be used without entering the interpreter interface.

3. File path

The most well-known way to pass code to Python is probably through the file path.

# specify the python file path
python spam.py
Copy the code

The key to doing this is to put the directory containing the file in sys.path. This way all your imports can continue to be used. But that’s why you can’t/shouldn’t pass in a module path contained in a package. Because sys.path may not contain the package’s directory, all imports will be in a different directory than what you expect for the package.

4. Use -m for packages

The correct way to execute a Python package is to use -m and specify the name of the package to run.

python -m spam
Copy the code

It uses runpy [5] underneath. To do this in your project, just specify a __main__.py file in the package, which will be executed as __main__. And a submodule can be imported just like any other module, so you can test it in a variety of ways.

I know some people like to write a main submodule in a package and write its __main__. Py as:

from . import main

if __name__ == "__main__":
    main.main()
Copy the code

Personally, I’m not a fan of the separate main modules, but put all the related code directly into __main__.py, because I feel the module names are redundant.

The author does not care about the “main” or “__main__” modules as entry files because they are executed using only their package names. I think this also implies that the entry module should not be imported by other modules. My last article [6] was more radical than the author, arguing that even the if statement should not be written.)

5, directories,

Defining __main__.py can also be extended to directories. If you look at the example that makes this blog post possible, Python News is executable because the news directory has a __main__.py file. The directory is executed by Python as a file path.

Now you might be asking, “Why not just specify the file path?” Well, frankly, there’s one thing we need to be clear about file paths. 😄 DURING publishing, I could simply write instructions to run Python news/announce.py, but there is no exact reason why such a mechanism exists.

Plus I can change the file name later and no one will notice. Plus I knew the code would come with auxiliary files, so it made sense to put it in a directory rather than as a single file.

Of course, I could have turned it into a package using -m, but there was no need because the Announce script was simple and I knew it would keep to a single self-contained file (less than 200 lines, and the test module was about the same length).

Also, the __main__.py file is very simple.

import runpy
# Change 'announce' to whatever module you want to run.
runpy.run_module('announce', run_name='__main__', alter_sys=True)
Copy the code

Now obviously you have to deal with dependencies, but if your script just uses the standard library or puts dependency modules next to __main__.py, then that’s enough!

I think the author is being a bit “showy” here, because this method requires that you know how to use runpy, but just like running a package with the -m argument, it also uses runpy at the bottom. __main__.py does not import the announce module, so it will execute as the main module.

6. Execute a compressed file

If you do have multiple files and/or dependency modules, and want to distribute all the code as a unit, you can take a __main__.py, place it in a compressed file, and put the compressed file in sys.path. Python will run __main__.py for you.

Pass a compressed package to Python
python app.pyz
Copy the code

People now customarily name such compressed files with.pyz file extensions, but this is purely traditional and doesn’t affect anything; You can also use the.zip file extension.

To simplify the creation of such executable compressed files, the standard library provides the zipApp [7] module. It generates __main__.py for you and adds a shebang line, so you don’t even need to specify Python if you don’t want to specify it on UNIX. If you want to move a bunch of pure Python code, this is a good way to do it.

Unfortunately, you can only run a compressed file like this if all the code it contains is pure Python. Executing compressed files does not work for extension modules (which is why SetupTools has a zip_safe [8] flag). Extension Module (C/C++)

To load the extension module, Python must call the dlopen() [9] function, which passes in a file path, but this obviously doesn’t work when the file path is included in the compressed file.

I know of at least one person who has spoken to the Glibc team about supporting passing memory buffers into compressed files so Python can read extension modules into memory and pass them to compressed files, but the glibc team doesn’t agree if memory serves this purpose.

But all hope is not lost! You can use a project such as Shiv [10], which bundles your code and then provides a __main__.py to handle extracting compressed files, caching, and executing the code for you. Although not as ideal as a pure Python solution, it works, and in this case is elegant.

(Translation is limited. I have added some notes to make it easier to read. Please search “Python Cat” to read more excellent original or translated works.)

Refer to the link

[0] snarky. Ca/the things – wa…

[1] marketplace.visualstudio.com/items?itemN…

[2] github.com/microsoft/v…

[3] pypi.org/project/tow…

[4] snarky. Ca/according to – you – sho…

[5] docs.python.org/3/library/r…

[6] mp.weixin.qq.com/s/1ehySR5NH…

[7] docs.python.org/3/library/z…

[8] setuptools. Readthedocs. IO/en/latest/s…

[9] linux.die.net/man/3/dlope…

[10] pypi.org/project/shi…