Does python's setuptools support the `__name__ == "__main__"` style of execution? - python

I'm just getting into packaging with setuptools, and it seems that the recommended way to install a python script along with one's module is to specify a script name that calls the name of a function, like this:
setup(
# ...
entry_points = {
"console_scripts": [
"script_name": "project.main:main",
],
}
)
This clearly precludes the standard way of making a python module executable, which is (last time I checked, which was a while ago) to use if __name__ == "__main__": do_stuff(). Does setuptools support this style, or should I switch to defining a main function and specifying it in entry_points?

It is: "script_name = project.main:do_stuff with setuptools
Setuptools creates scripts named script_name that imports and runs the function project.main:do_stuff, not run the script directly. You should re-read this part (alternate link, if you use Distribute) of the setuptools docs again to understand why it's this way. The script it creates contains if __name__ == "__main__" still. So yes, this is still the defacto way of making it execute.
This is a copy of easy_install installed with setuptools
#!/usr/bin/python
# EASY-INSTALL-ENTRY-SCRIPT: 'distribute==0.6.14','console_scripts','easy_install'
__requires__ = 'distribute==0.6.14'
import sys
from pkg_resources import load_entry_point
if __name__ == '__main__':
sys.exit(
load_entry_point('distribute==0.6.14', 'console_scripts', 'easy_install')()
)
I think it's best to define an entry point and a script similar to easy_install. That imports and uses the entry point, like you show in your example if __name__ == "__main__": do_stuff(). It's great for debugging and early testing, also if you use distutils, there is no need to add/change anything. You can also have another app to call do_stuff() to access your app with out the overhead of running it in the shell, which is what setuptools is doing, distutils copy's the script.

Related

Is it possible to ensure a package's dependencies have been installed before running it's setup.py?

I'm working on distributing a Python package. It depends on the library lupa. I want to run a post-install script that depends on lupa that initializes some data within the package after it's been installed. After looking at some answers around StackOverflow, my stripped setup.py essentially looks like this:
# setup.py
from distutils.core import setup
from setuptools.command.install import install
class PostInstallCommand(install):
def run(self):
# Do normal setup
install.run(self)
# Now do my setup
from mymodule.script import init
init()
setup(
# ...
install_requires = [
# ...
"lupa >= 1.10",
# ...
],
cmdclass = {
'install': PostInstallCommand
}
)
However, when emulating a fresh install/setup with tox on Python 3.10, I get this error:
File "C:\My\Computer\Temp\pip-req-build-pl0jria3\setup.py", line 26, in run
from mymodule.script import init
File "C:\My\Computer\Temp\pip-req-build-pl0jria3\mymodule\script.py", line 28, in <module>
import lupa
ModuleNotFoundError: No module named 'lupa'
I was under the impression that anything put into install_requires would be installed by the time setup() finished, but that appears not to be the case (also corroborated by this answer). Is there anything I can do to ensure that lupa is installed prior to mymodule.script.init(), or is that stage of the setup process entirely out of the user's hands?
After doing a good bit of research, it seems this kind of post-install script is somewhat against the core philosophy of setuptools, which means that a request like this is unlikely to be added, or at least added anytime soon.
Fortunately, this is somewhat of a blessing in disguise; my post-install script is actually an "update" console entry point that the user calls anytime they've added mods or updated any of the packages data. This script can be (and is supposed to be) called many times by the user, so by having it as part of the install process it helps introduce the purpose of the script to the user right from the start. This makes the slight annoyance on install tolerable, at least in my circumstance.

Python distutils.core.run_setup fails when running from within script?

I'm trying to run a Python setup script from within a python script. That is, instead of:
python path_to/setup.py build --build-base="build_dir" install --prefix="install_dir"
which works fine. I want to be able to call
from distutils.core import run_setup
args = ['build', '--build-base="build_dir"', 'install', '--prefix="install_dir"']
run_setup("path_to/setup.py", script_args=args, stop_after='run')
from within another Python script (as automated/on the fly installation). This does not seem to work at the moment as the call returns the error
RuntimeError: 'distutils.core.setup()' was never called
-- perhaps 'setup.py' is not a Distutils setup script?
which is strange since calling it from outside the script works. Is there something wrong with this approach? The setup.py script looks normal:
from distutils.core import setup
from distutils.command.build_py import build_py
# ...
if __name__ == '__main__':
setup( ... )
Alternatively, I'm thinking about just calling "subprocess" from the script, but then want to ensure I'm using the same Python instance/runtime as the running Python instance (in case many python versions are installed). How could I do this, ensure to call the top command from a subprocess with the same python?

How does this python code work?

I have just started writing some simple scripts in python as I've started using ubuntu as my default operating system.
So I came across the code for mpsyt (terminal youtube player). I was surprised how simple the coding was. Could anybody explain what is going on here? I don't undestand how a seemingly complex program could have such a small amount of code....
#!/usr/bin/python3
# EASY-INSTALL-ENTRY-SCRIPT: 'mps-youtube==0.2.5','console_scripts','mpsyt'
__requires__ = 'mps-youtube==0.2.5'
import sys
from pkg_resources import load_entry_point
if __name__ == '__main__':
sys.exit(
load_entry_point('mps-youtube==0.2.5', 'console_scripts', 'mpsyt')()
)
This is an autogenerated stub; it executes an entry point in the mps-youtube package installed in your site-packages. It is not the actual script.
The setuptools project installs such stubs in the bin directory; it's task is to find the right version of the project and load the real script.
There will be a matching lib/python[version]/mps-youtube-0.2.5-py[version].egg-info directory holding metadata, including an entry_points.txt file that contains INI-file-format style information on the package. The load_entry_point('mps-youtube==0.2.5', 'console_scripts', 'mpsyt') line will look for that exact file to load the mpsyt definition from the console_scripts section.
In the [console_scripts] section there will be a mpsyt entry that names the actual module used to run the script. Judging by the project setup.py file that'll look like this:
[console_scripts]
mpsyt = mps_youtube:main.main
pointing to the mps_youtube.main module, where the main() function will be called to do the actual work; look for a lib/python[version]/mps_youtube/main.py file, then search for a def main(): function definition. From Github you can see the actual script is a little longer.
This isn't the entire program. This just runs the rest of the program.

python module and command line program

I have a code which I'd like people to be able to use as a stand alone python program, or to import as a module for their own codes. Is there a way to package a module which can also include a program that can be run from the command line?
I.e. from the command-line:
> ZHermesCode
or within ipython or a python script:
import ZHermesCode
Look up Setuptools automatic script creation. For example, python-scss uses this technique to make scss an executable shell command.
In setup.py:
setup(
# ...
entry_points={
'console_scripts': [
'scss = scss.tool:main',
]
},
)
Then defining a function main in scss/tool.py. It also uses the technique mentioned by Loocid to make the tool.py file itself directly executable (as opposed to the wrapper script that is publicly installed by setuptools according to the recipe above):
if __name__ == '__main__':
main()
If you use:
if name == '__main__':
The code held in that 'if' statement will only be ran if someone runs your program from the command line.
If someone was to import your module instead, the code will not run.
Eg
def test(st):
return(st)
if name == "__main__":
print(test("hello"))
If you run this program from the command line, it will print "hello". However, someone could also import this module and have access to the "test" function to use in their own programs.

How to install python module as a command line application under windows?

I need to install a python module in the site packages that also will be used as a command line application. Suppose I have a module like:
app.py
def main():
print 'Dummy message'
if __name__ == '__main__':
main()
setup.py
import distutils
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
if __name__ == '__main__':
setup(name = 'dummy',
version = '1.0',
packages = ['dummy'],
)
Creating the dist by:
setup.py sdist
Install:
setup.py install
And now I would like to use it as a command line application by opening the command window and typing just: dummy
Is it possible to create such application under windows without to carry out registering system pat variables and so on ...
You can use the options in setup.py to declare command line scripts. Please refer to this article. On Windows, the script will be created in "C:\Python26\Scripts" (if you didn't change the path) - lots of tools store their scripts there (e.g. "easy_install", "hg", ...).
Put the following in dummy.cmd:
python.exe -m dummy
Or is it dummy.app...
Oh well, it's one of those.

Categories