Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

is there a way to run xonsh from .py script as .xsh script? #2478

Closed
andry81 opened this issue Aug 13, 2017 · 22 comments
Closed

is there a way to run xonsh from .py script as .xsh script? #2478

andry81 opened this issue Aug 13, 2017 · 22 comments
Labels

Comments

@andry81
Copy link

andry81 commented Aug 13, 2017

I want to pack xonsh instructions under the .py extension to run xonsh as usual python script:
python myscript.py

There myscript.py is something like this:

if __name__ == '__main__':
	if not 'builtins' in globals() or not hasattr(builtins, '__xonsh_env__'):
		from xonsh import main as xonsh
		# some magic to force treat `myscript.py` as `myscript.xsh`
		xonsh.main()

if 'builtins' in globals() and hasattr(builtins, '__xonsh_env__'):
	print('hello xonsh\n')
	dir /A:D /B
	1 + 1

Is there a way to do so?

@AstraLuma
Copy link
Member

Xonsh scripts must be compiled as xonsh in order to run.

However, importing xonsh.imphooks makes .xsh files importable.

@andry81
Copy link
Author

andry81 commented Aug 13, 2017

I've found closest solution:

if __name__ == '__main__':
	import builtins
	if not 'builtins' in globals() or not hasattr(builtins, '__xonsh_shell__'):
		from xonsh import main as xonsh
		import sys
		sys.argv.insert(0, __file__)
		print('main')
		xonsh.main(sys.argv)
		sys.exit()

#import builtins
if 'builtins' in globals() and hasattr(builtins, '__xonsh_shell__'):
	print('hello xonsh\n')
	evalx('dir /A:D /B "*.*"')
	print(evalx('1 + 1'))

It works for both:
python test.py
python -m xonsh test.py

Is there a better way without the evalx? Or may be through the @ without quotes?

@AstraLuma
Copy link
Member

Write a trampoline script that imports xonsh.imphooks and then calls to an .xsh file.

@andry81
Copy link
Author

andry81 commented Aug 15, 2017

It's a bit strange to use .xsh through the .py. Why to do that if you can directly call to .xsh? It has no sense.

@AstraLuma
Copy link
Member

If you want to execute xonsh scripts directly, you have to use xonsh.

If you want to call xonsh code from python, you must first bootstrap the xonsh parser.

@andry81
Copy link
Author

andry81 commented Aug 15, 2017

I don't get the difference. Is that call is a call from python or using xonsh?
python -m xonsh test.xsh

My point was to use single file of .py extension for all things including xonsh code. Using 2 extensions has no sense because i can call .xsh directly then.

@AstraLuma
Copy link
Member

python -m xonsh works for xonsh, if the python environment has xonsh installed.

Xonsh can perfectly understand xonsh and python files.

@andry81
Copy link
Author

andry81 commented Aug 15, 2017

Then why i need to bootstrap anything?

@AstraLuma
Copy link
Member

Your original question was to get python myscript.py working.

@andry81
Copy link
Author

andry81 commented Aug 15, 2017

Yes. But then i have to move a half of logic to another file with .xsh extension instead of use the .xsh file as a single file. It's not nice alternative i think.

@AstraLuma
Copy link
Member

Sorry, we don't have a magic way to recompile the file in the middle of its own execution.

@andry81
Copy link
Author

andry81 commented Aug 15, 2017

But why not? Is there a function to do so? Or may be i am asking too much?

@AstraLuma
Copy link
Member

  1. Use the subset of xonsh that's still parsable as Python (throwing away command execution and macros)
  2. Python compiles the file to bytecode and begins executing
  3. The module calls a function, which reparses and recompiles the file as xonsh, somehow manages to move the current instruction to the new bytecode, while transitioning all of the state. Without any bugs.
  4. Execution continues as if nothing happened.

In summary: That's a research thesis, and we're a small team of volunteers.

@andry81
Copy link
Author

andry81 commented Aug 15, 2017

I thought it could be solved somehow via on runtime self compilation to different bytecode w/o any injection to existing one and start execution a new one from beginning. But of cause it has sense only when the runtime runs from a script.
Anyway, thx for the clarification!

@AstraLuma
Copy link
Member

If you know enough about Python interpreter internals to make it happen, pull requests are always welcome.

@gforsyth
Copy link
Collaborator

Closing this as I believe the question is answered, but please re-open if there are further points/questions!

@v217
Copy link

v217 commented Sep 26, 2019

There is no editor/ide support for xonsh xsh files. So is it possible to import some of the conveniences of xonsh into python, what I mean is eg:

from xonsh import xonsh_subprocess_run

So you don't get the convenient syntax of xonsh but just a few of it's convenient functions etc.
I don't want to learn about a second package like eg:
http://amoffat.github.io/sh/
or
plumbum
just for scripts.
I couldn't find docs on this. I guess for python experts the way to do this is obvious.
So far I found
file:///usr/share/doc/xonsh-doc/html/api/lib/subprocess.html

@scopatz
Copy link
Member

scopatz commented Oct 1, 2019

This is somewhat undocumented, but if you want to start up a xonsh environment, for 3rd party packages, I often put the following in the __init__.py file, at the very top:

from xonsh.main import setup
setup()
del setup

This is safely reentrant and will set up the xonsh session object for you, if it hasn't been already. Here is an example in the wild: https://github.com/regro/rever/blob/master/rever/__init__.py

@v217
Copy link

v217 commented Oct 2, 2019

Thank you, this works!

@scopatz
Copy link
Member

scopatz commented Oct 2, 2019

Awesome! We should really add this to the docs....

@v217
Copy link

v217 commented Oct 3, 2019

Here my documentation effort:
I am not sure about the best place in the docs - maybe just a link to this issue.
I have summarized your answer, so a python novice unfamiliar with python module conventions understands how to use xonsh modules in python.

Can I call xonsh from my python project?

Sure, suppose you have some xonsh files file1.xsh, file2.xsh and the file __init__.py with the following content:

# __init__.py
from xonsh.main import setup
setup()
del setup

in the directory my_xonsh_directory which must be available in your PYTHON_PATH eg. in the same directory like your python_file.py:

./python_file.py

./my_xonsh_directory

./my_xonsh_directory/__init__.py
./my_xonsh_directory/file1.xsh
./my_xonsh_directory/file2.xsh

then you can import eg. file1.xsh in python_file.py exactly like you would import a normal python file:

# ./python_file.py
from my_xonsh_directory import file1

@scopatz
Copy link
Member

scopatz commented Oct 3, 2019

Thanks, I have already opened #3332 for more docs here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants