Why python subprocess stops executing when it runs a certain command? - python

I want to automate certain tasks during development. This is a part of one of those tasks. I'm trying to install django automatically with pipenv.
import os
import subprocess
def run(project):
os.mkdir(project)
os.chdir(project)
subprocess.run("pipenv install django".split())
os.mkdir("io")
os.chdir("io")
subprocess.run("pipenv shell".split())
subprocess.run("django-admin startproject _django".split())
print(f"Created new django project at {project}/io/_django")
if __name__ == '__main__':
run("hello-world")
When i run this code it execute up to the line:
subprocess.run("pipenv shell".split())
but doesn't run the following lines.
Why is that? And how to solve it?

It's because pipenv shell spawns an interactive shell and it expects input on stdin.
You’d be better using pipenv run to run commands in your virtual environment: pipenv run django-admin ...
P.S.: you can remove .split(), subprocess.run can work with command in form of the string. Use explicit array of arguments in case you need to be able to include space as a character inside of an argument, or when working with untrusted (user-supplied) arguments.

The final curated code:
(Credits to #Andrew Morozko)
import os
import subprocess
def run(project):
os.mkdir(project)
os.chdir(project)
subprocess.run("pipenv install django")
os.mkdir("io")
os.chdir("io")
subprocess.run("pipenv run django-admin startproject _django")
print(f"Created new django project at {project}/io/_django")
if __name__ == '__main__':
run("hello-world")

Related

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")

Python Tornado AttributeError: module 'test' has no attribute '__path__'

I am attempting to just run the Hello World code from Tornado docs
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
def make_app():
return tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
Except I am getting an error: AttributeError: module 'test' has no attribute '__path__'
I am just using IDLE to run test.py
I thought this was due to my Windows 10 computer not having Python accessible to PATH but even with adding in the python 3.6 to PATH I am still getting the same error. Any ideas?
The screenshot is how I added python to PATH and I think I got it correct..
------EDIT------
Ill add some screenshots of the errors/tracebacks I am running into. 1st one is the command prompt below when the test.py is ran in IDLE 3.6 in Windows 10.
If there is an import error, I can import Tornado just fine thru IDLE interpreter.
I also tried running this hello World code in IPython 3.7, and I get this error:
Solution: Run your file without the -m argument.
Another solution would be to provide the file name without the .py extension:
python -m test
This will also work.
Explanation:
The -m argument tells Python to run a module (file) present in the Python path. It doesn't take the name of the file, it takes the name of the module. The difference is that the file name contains .py suffix, whereas the module name doesn't.
So you can run the test.py file like this, too: python -m test.
When to use -m argument:
The -m argument is there for convenience. For example, if you want to run python's default http server (which comes with python), you'd write this command:
python -m http.server
This will start the http server for you. The convenience that -m argument gives you is that you can write this command from anywhere in your system and python will automatically look for the package called http in your the system's Path.
Without the -m argument, if you wanted to run the http server, you'd have to give it's full path like:
python C:\path\to\python\installation\http\server.py
So, -m argument makes it easy to run modules (files) present in the Path.
With Tornado would you happen to know how to kill the Python interpreter? A CNTRL-C doesn't do anything.
I use Linux and Ctrl-C works fine for me. On Windows you can try Ctrl-D or Ctrl-Z. Or here are some answers: Stopping python using ctrl+c

executing standalone fabric script by calling it by its name, without the .py extension

I have a fabric script called fwp.py that I run without calling it throug fab by using:
if __name__ == '__main__':
# imports for standalone mode only
import sys
import fabric.main
fabric.main.main(fabfile_locations=[__file__])
The thing is then have to call the script by calling fwp.py. I'd like to rename it as fwp to be able to call it as fwp. But doing that would result in
Fatal error: Couldn't find any fabfiles!
Is there a way to make Python/Fabric import this file, despite the lack of a ".py" extension?
To reiterate and clarify:
I'm not using the "fab" utility (e.g. as fab task task:parameter); just calling my script as fwp.py task task:parameter, and would like to be able to call it as fwp task task:parameter.
Update
It's not a duplicate of this question. The question is not "How to run a stand-alone fabric script?", but "How to do so, while having a script without a .py" extension.
EDIT: Original answer corrected
The fabric.main.main() function automatically adds .py to the end of supplied fabfile locations (see https://github.com/fabric/fabric/blob/master/fabric/main.py#L93). Unfortunately that function also uses Python's import machinery to load the file so it has to look like a module or package. Without reimplementing much of the fabric.main module I don't think it will be possible. You could try monkey-patching both fabric.main.find_fabfiles and fabric.main.load_fabfiles to make it work.
Origininal answer (wrong)
I can get this to work unaltered on a freshly installed fabric package. The following will execute with a filename fwp and executable permission on version 1.10.1, Python2.7. I would just try upgrading fabric.
#!/usr/bin/env python
from fabric.api import *
import fabric.main
def do():
local('echo "Hello World"')
if __name__ == '__main__':
fabric.main.main(fabfile_locations=[__file__])
Output:
$ ./fwp do
Hello World
Done

Changing dir to execute program in linux

I am trying to execute a program from a directory
import os
os.chdir("/home/user/a/b")
with cd("/home/user/a/b"):
run ("./program")
i get cd is not defined...
any help appreciated cheers
I'm not sure what instructions you're following to get what you showed. There is no built-in function called cd or run in Python.
You can call a program in a specific directory using the subprocess module:
import subprocess
subprocess.call("./program", cwd="/home/user/a/b")
The cwd argument causes the call function to automatically switch to that directory before starting the program named in the first argument.
It looks like you are trying to use functionalities of fabric. Make sure fabric is installed, and cd and run are imported from fabric. Something like,
from fabric.context_managers import cd
from fabric.operations import run
import os
os.chdir("/home/user/a/b")
with cd("/home/user/a/b"):
run ("./program")
Save your file as fabfile.py,and from the same directory run it as:
fab -H localhost
For more information on the fabric, checkout: fabric

how to start django shell with ipython in qtconsole mode?

When i start django shell by typing python manage.py shell
the ipython shell is started. Is it possible to make Django start ipython in qtconsole mode? (i.e. make it run ipython qtconsole)
Arek
edit:
so I'm trying what Andrew Wilkinson suggested in his answer - extending my django app with a command which is based on original django shell command. As far as I understand code which starts ipython in original version is this:
from django.core.management.base import NoArgsCommand
class Command(NoArgsCommand):
requires_model_validation = False
def handle_noargs(self, **options):
from IPython.frontend.terminal.embed import TerminalInteractiveShell
shell = TerminalInteractiveShell()
shell.mainloop()
any advice how to change this code to start ipython in qtconsole mode?
second edit:
what i found and works so far is - start 'ipython qtconsole' from the location where settings.py of my project is (or set the sys.path if starting from different location), and then execute this:
import settings
import django.core.management
django.core.management.setup_environ(settings)
and now can i import my models, list all instances etc.
The docs here say:
If you'd rather not use manage.py, no problem. Just set the
DJANGO_SETTINGS_MODULE environment variable to mysite.settings and run
python from the same directory manage.py is in (or ensure that
directory is on the Python path, so that import mysite works).
So it should be enough to set that environment variable and then run ipython qtconsole. You could make a simple script to do this for you automatically.
I created a shell script with the following:
/path/to/ipython
qtconsole --pylab inline -c "run /path/to/my/site/shell.py"
You only need the --pylab inline part if you want the cool inline matplotlib graphs.
And I created a python script shell.py in /path/to/my/site with:
import os
working_dir = os.path.dirname(__file__)
os.chdir(working_dir)
import settings
import django.core.management
django.core.management.setup_environ(settings)
Running my shell script gets me an ipython qtconsole with the benefits of the django shell.
You can check the code that runs the shell here. You'll see that there is no where to configure what shell is run.
What you could do is copy this file, rename it as shell_qt.py and place it in your own project's management/commands directory. Change it to run the QT console and then you can run manage.py shell_qt.
Since Django version 1.4, usage of django.core.management.setup_environ() is deprecated. A solution that works for both the IPython notebook and the QTconsole is this (just execute this from within your Django project directory):
In [1]: from django.conf import settings
In [2]: from mydjangoproject.settings import DATABASES as MYDATABASES
In [3]: settings.configure(DATABASES=MYDATABASES)
Update: If you work with Django 1.7, you additionally need to execute the following:
In [4]: import django; django.setup()
Using django.conf.settings.configure(), you specify the database settings of your project and then you can access all your models in the usual way.
If you want to automate these imports, you can e.g. create an IPython profile by running:
ipython profile create mydjangoproject
Each profile contains a directory called startup. You can put arbitrary Python scripts in there and they will be executed just after IPython has started. In this example, you find it under
~/.ipython/profile_<mydjangoproject>/startup/
Just put a script in there which contains the code shown above, probably enclosed by a try..except clause to handle ImportErrors. You can then start IPython with the given profile like this:
ipython qtconsole --profile=mydjangoproject
or
ipython notebook --profile=mydjangoproject
I also wanted to open the Django shell in qtconsole. Looking inside manage.py solve the problem for me:
Launch IPython qtconsole, cd to the project base directory and run:
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
Dont forget to change 'myproject' to your project name.
You can create a command that extends the base shell command and imports the IPythonQtConsoleApp like so:
create file qtshell.py in yourapp/management/commands with:
from django.core.management.commands import shell
class Command(shell.Command):
def _ipython(self):
"""Start IPython Qt console"""
from IPython.qt.console.qtconsoleapp import IPythonQtConsoleApp
app = IPythonQtConsoleApp.instance()
app.initialize(argv=[])
app.start()
then just use python manage.py qtshell
A somewhat undocumented feature of shell_plus is the ability to run it in "kernel only mode". This allows us to connect to it from another shell, such as one running qtconsole.
For example, in one shell do:
django-admin shell_plus --kernel
# or == ./manage.py shell_plus --kernel
This will print out something like:
# Shell Plus imports ...
...
To connect another client to this kernel, use:
--existing kernel-23600.json
Then, in another shell run:
ipython qtconsole --existing kernel-23600.json
This should now open a QtConsole. One other tip, instead of running another shell, you can also hit Ctrl+Z, and run bg to tell current process to run in background.
You can install django extensions and then run
python manage.py shell_plus --ipython

Categories

Resources