how to execute python function from module without pip install - python

How can I execute python function from a package without main / pip install in editable mode
e.g.
my sample.py is
def cli():
print("I'm running myTest")
return 0
and setup.py as
setup(
name="sample",
version="1.0",
py_modules=["sample"],
include_package_data=True,
entry_points="""
[console_scripts]
sample=sample:cli
""",
)
now if I do pip install -e . and execute sample
I get desired o/p as
I'm running myTest
However with this I'm not able to debug it,
What's ideal way to execute function from a module without pip install / using another python file,
also tried with python -m sample but that gives no o/p since there is no entrypoint

So call your function in your sample.py:
def cli():
print("I'm running myTest")
return 0
if __name__ == "__main__":
cli()
Then, from the command line:
python sample.py
See here for what if __name__ == "__main__": means:
https://www.freecodecamp.org/news/if-name-main-python-example/
If you want to leave sample.py completely unmodified, then you just create a new file test_sample.py and import sample to use it like so:
import sample
sample.cli()
or, as an alternative method of import, test_sample.py can also be:
from sample import cli
cli()
Then you run:
python test_sample.py
Just make sure test_sample.py is in the same directory as sample.py.

Related

Cannot execute function in python setup.py [duplicate]

Is it possible to specify a post-install Python script file as part of the setuptools setup.py file so that a user can run the command:
python setup.py install
on a local project file archive, or
pip install <name>
for a PyPI project and the script will be run at the completion of the standard setuptools install? I am looking to perform post-install tasks that can be coded in a single Python script file (e.g. deliver a custom post-install message to the user, pull additional data files from a different remote source repository).
I came across this SO answer from several years ago that addresses the topic and it sounds as though the consensus at that time was that you need to create an install subcommand. If that is still the case, would it be possible for someone to provide an example of how to do this so that it is not necessary for the user to enter a second command to run the script?
Note: The solution below only works when installing a source distribution zip or tarball, or installing in editable mode from a source tree. It will not work when installing from a binary wheel (.whl)
This solution is more transparent:
You will make a few additions to setup.py and there is no need for an extra file.
Also you need to consider two different post-installations; one for development/editable mode and the other one for install mode.
Add these two classes that includes your post-install script to setup.py:
from setuptools import setup
from setuptools.command.develop import develop
from setuptools.command.install import install
class PostDevelopCommand(develop):
"""Post-installation for development mode."""
def run(self):
develop.run(self)
# PUT YOUR POST-INSTALL SCRIPT HERE or CALL A FUNCTION
class PostInstallCommand(install):
"""Post-installation for installation mode."""
def run(self):
install.run(self)
# PUT YOUR POST-INSTALL SCRIPT HERE or CALL A FUNCTION
and insert cmdclass argument to setup() function in setup.py:
setup(
...
cmdclass={
'develop': PostDevelopCommand,
'install': PostInstallCommand,
},
...
)
You can even call shell commands during installation, like in this example which does pre-installation preparation:
from setuptools import setup
from setuptools.command.develop import develop
from setuptools.command.install import install
from subprocess import check_call
class PreDevelopCommand(develop):
"""Pre-installation for development mode."""
def run(self):
check_call("apt-get install this-package".split())
develop.run(self)
class PreInstallCommand(install):
"""Pre-installation for installation mode."""
def run(self):
check_call("apt-get install this-package".split())
install.run(self)
setup(
...
P.S. there are no any pre-install entry points available on setuptools. Read this discussion if you are wondering why there is none.
Note: The solution below only works when installing a source distribution zip or tarball, or installing in editable mode from a source tree. It will not work when installing from a binary wheel (.whl)
This is the only strategy that has worked for me when the post-install script requires that the package dependencies have already been installed:
import atexit
from setuptools.command.install import install
def _post_install():
print('POST INSTALL')
class new_install(install):
def __init__(self, *args, **kwargs):
super(new_install, self).__init__(*args, **kwargs)
atexit.register(_post_install)
setuptools.setup(
cmdclass={'install': new_install},
Note: The solution below only works when installing a source distribution zip or tarball, or installing in editable mode from a source tree. It will not work when installing from a binary wheel (.whl)
A solution could be to include a post_setup.py in setup.py's directory. post_setup.py will contain a function which does the post-install and setup.py will only import and launch it at the appropriate time.
In setup.py:
from distutils.core import setup
from distutils.command.install_data import install_data
try:
from post_setup import main as post_install
except ImportError:
post_install = lambda: None
class my_install(install_data):
def run(self):
install_data.run(self)
post_install()
if __name__ == '__main__':
setup(
...
cmdclass={'install_data': my_install},
...
)
In post_setup.py:
def main():
"""Do here your post-install"""
pass
if __name__ == '__main__':
main()
With the common idea of launching setup.py from its directory, you will be able to import post_setup.py else it will launch an empty function.
In post_setup.py, the if __name__ == '__main__': statement allows you to manually launch post-install from command line.
Combining the answers from #Apalala, #Zulu and #mertyildiran; this worked for me in a Python 3.5 environment:
import atexit
import os
import sys
from setuptools import setup
from setuptools.command.install import install
class CustomInstall(install):
def run(self):
def _post_install():
def find_module_path():
for p in sys.path:
if os.path.isdir(p) and my_name in os.listdir(p):
return os.path.join(p, my_name)
install_path = find_module_path()
# Add your post install code here
atexit.register(_post_install)
install.run(self)
setup(
cmdclass={'install': CustomInstall},
...
This also gives you access the to the installation path of the package in install_path, to do some shell work on.
I think the easiest way to perform the post-install, and keep the requirements, is to decorate the call to setup(...):
from setup tools import setup
def _post_install(setup):
def _post_actions():
do_things()
_post_actions()
return setup
setup = _post_install(
setup(
name='NAME',
install_requires=['...
)
)
This will run setup() when declaring setup. Once done with the requirements installation, it will run the _post_install() function, which will run the inner function _post_actions().
If using atexit, there is no need to create a new cmdclass. You can simply create your atexit register right before the setup() call. It does the same thing.
Also, if you need dependencies to be installed first, this does not work with pip install since your atexit handler will be called before pip moves the packages into place.
I wasn't able to solve a problem with any presented recommendations, so here is what helped me.
You can call function, that you want to run after installation just after setup() in setup.py, like that:
from setuptools import setup
def _post_install():
<your code>
setup(...)
_post_install()

Python module cli bash: command not found

I'm trying to create a cli tool using python but whenever I try to run the command in terminal i get the error 'bash: command not found'. The python modules installs without any errors. I'm on macos and using python version: 3.9.4. I think this may be a PATH issue but currently unsure.
Here is the setup.py:
from setuptools import setup
setup(
name = 'mycommand',
version = '0.1.0',
packages = ['mycommand'],
entry_points = {
'console_scripts': [
'mycommand = mycommand.__main__:main'
]
}
)
Here is the main.py:
import sys
def main():
print('in main')
args = sys.argv[1:]
print('count of args :: {}'.format(len(args)))
for arg in args:
print('passed argument :: {}'.format(arg))
if __name__ == '__main__':
main()
Hope somebody can help me with this.
This is the tutorial I have followed: https://trstringer.com/easy-and-nice-python-cli/
if your layout is like that:
setup.py
mycommand/main.py
then the entrypoint is simply mycommand.main:main:
mycommand/main.py -> mycommand.main
:main -> def main()
You need to use the scripts keyword argument.
First add a file called mycommand (note that you should not have any extension) in the same directory as main.py
#!/usr/bin/env python
from mycommand.main import main
main()
then edit your setup.py like this:
from setuptools import setup
setup(
name = 'mycommand',
version = '0.1.0',
packages = ['mycommand'],
scripts=['mycommand/mycommand'],
)
Then when you install this package you will be able to call mycommand

How do I create terminal commands in my python script?

I want to create terminal commands in my python script.
for example,
$ cd my_project_folder
$ --help (I use a command in this python folder)
outputs
$ this is the help command. You've activated it using --help. This is only possible because you cd'd to the folder and inputted this command.
I am looking for user defined commands ( ones that I've already defined in my python function.)
I am not looking for commands like 'ls' and 'pwd'.
You can use os.system, as below:
import os
os.system('your command')
Use os module if you wanna execute command that is specific to bash or shell that you use
import os
os.system("string command")
As Leemosh sugested you can use click module to make your own commands that are related to scripts
And you can also use sys module to read args that you put affter your script
example
$ python3 name_of_script.py arg_n
import sys
if sys.argv[1] == "commnd":
do something
What you are looking for is creating a setup.py file and defining some entry points. I'd recommend watching https://www.youtube.com/watch?v=GIF3LaRqgXo&ab_channel=CodingTech or https://www.youtube.com/watch?v=4fzAMdLKC5ks&ab_channel=NextDayVideo to better understand how to deal with setup.py.
from setuptools import setup
setup(
name="Name_of_your_app",
version="0.0.1",
desciption="some description",
py_modules=["app"], # I think you don't need that
package_dir={"": "src"}, # and probably you don't need this as well
entry_points={"console_scripts": {"THIS_IS_THE_DEFINED_COMMAND=app:main"}},
)
app is name of the py file, main is function you wanna call.
app.py
def main():
print("hello")

Trying to find the Git Branch a repo is on and pass it into a requirements.txt

I am currently splitting out our large BDD project and moving the test code into the various applications so the BDD tests and the Application code are all in the same location.
As part of this I have created a library for shared code and pointed the requirements.txt file to this using
-e git+git#gitlab.xx.xxx:xxxxxxxx/xxxxxxBDD.git#"branchID(e.g develop)#egg=BehaveModules
Currently I am reliant on manually entering the Branch ID, so if changes are being made to the common library in line with the branch of code being developed I need to remember to update the branch ID.
Is there a way of doing this problematically and then pass this into my requirements.txt
for instance
Application_branch = get current git branch
if Application_branch present in BDD_Library Repo:
branch = Application_branch
else:
branch = Develop
return branch
-e git+git#gitlab.xx.xxx:xxxxxxxx/xxxxxxBDD.git#branch#egg=BehaveModules
Instead of specifying variables in requirements.txt, You could create an install.py script and call pip by script.
import subprocess
import sys
_all_ = [
"git+https://github.com/googleapis/python-ndb.git#{}#egg=google_cloud_ndb",
"requests"
]
latest_branch = 'master'
def install(packages):
for package in packages:
if 'git+' in package:
subprocess.call([sys.executable, "-m", "pip", "install", package.format(latest_branch)])
else:
subprocess.call([sys.executable, "-m", "pip", "install", package])
if __name__ == '__main__':
install(_all_)
Instead of subprocess you can also use pip if you want:
import pip
def install(packages):
for package in packages:
pip.main(['install', package])

Why does "eggsecutable" search for __main__

I try to make a executable *.egg file. I can create this using the following method: I just put a __main__.py at the top-level of an .egg named .zip, and python will run that __main__.py
I have read that there is a more elegant way:
setup(
# other arguments here...
entry_points={
'setuptools.installation': [
'eggsecutable = my_package.some_module:main_func',
]
}
)
https://setuptools.readthedocs.io/en/latest/setuptools.html#eggsecutable-scripts
But if I create ( with run setup.py bdist_egg) and run the *.egg, it prints:
C:\Python27\python.exe: can't find '__main__' module in <eggpath>
So python doesn't find the entry point.
Is it possible make an executable egg without explicit __main__.py?
System:
Win 7
Python 2.7.9
setuptools 39.0.1 from c:\python27\lib\site-packages (Python 2.7))
UPDATE
I have tried both on Linux both with python3 and I got the same error.
It seems like the entry points documentation is misleading and you don't need them.
What you probably want something like this:
setup.py:
import setuptools
setuptools.setup(
name="example_pkg",
version="0.0.1",
# all the other parameters
# function to call on $ python my.egg
py_modules=['example_pkg.stuff:main']
)
example_pkg/stuff.py
def main():
print("egg test")
if __name__ == '__main__':
main()
create the egg: setup.py bdist_egg
run the egg: python dist\example_pkg-0.0.1-py3.6.egg
output: egg test
solution source: https://mail.python.org/pipermail/distutils-sig/2015-June/026524.html

Categories

Resources