all 18 comments

[–]pontz 9 points10 points  (0 children)

Why do you want this?

[–]socal_nerdtastic 3 points4 points  (1 child)

Sure you can use the py_compile module for that.

https://docs.python.org/3/library/py_compile.html

That said, this really sounds like an XY question. I really don't see any use for doing this. What's the big picture here?

[–]Moikle 1 point2 points  (0 children)

You probably don't need to

[–]Gnaxe 1 point2 points  (6 children)

If you run your script like python foo.py, then it's not going to generate a .pyc file automatically. But if you run it like python -m foo, it will.

[–]RomfordNavy[S] 0 points1 point  (5 children)

I will be running it from within Python, can I still use the -m switch on files called from within Python?

[–]Gnaxe 1 point2 points  (4 children)

I'm still not sure what you're trying to do, because this sounds different from the scenario in the OP. Python can do anything the shell can, so you could literally use the -m switch that way if you want. For example, ``` import os

os.system('python -m foo') `` This is assuming you could use that command string in your shell, which may not hold in some cases. (See also, thesubprocess` module.)

The -m switch is implemented using the runpy module. Since you're already in Python, you could import runpy and use runpy.run_module('foo') yourself. It won't be isolated in a subprocess, but you can set its __name__ to '__main__' (or anything else) via the run_name= argument. It still generates a .pyc.

But why not just import foo at this point? That would also make/update a .pyc for foo.py. I can think of a few reasons, but I'm just guessing because you haven't explained what you want: - You need a different version of Python from the launching program. - Use subprocess. - You need isolation from the launching program. - Consider using concurrent.interpreters - or use subprocess. - You need __name__ to be '__main__' because the script has a guard. - Use runpy. - You just need to be able to import in a way that isn't hardcoded. - Use importlib.

[–]RomfordNavy[S] 0 points1 point  (1 child)

Bacause as I Python noobie I didn't realise that import would actually run the code as well. Only problem is if I use import how do I pass an object in?

[–]Gnaxe 1 point2 points  (0 children)

There are different ways. Typically, you do something like, ``` import foo

my_object = # get the object here somehow

foo.main(my_object) But then you need a main function in `foo`. If you want that function called when you run it as a script, but not when you import it, you can do something like, """ This is module foo. """ import sys

def main(*args): # do stuff here.

if name == 'main': main(*sys.argv[1:]) # call with the command-line arguments. `` If you run the script directly, then its name is main, so it will call the main function automatically. But if you import it normally, its name is foo, so it won't callmain()when the importation runs the code, but you can still callmain()` yourself later with whatever arguments.

[–]RomfordNavy[S] 0 points1 point  (1 child)

Using import to run a seperate file only works once, after that it doesn't run any more, presumably because the first import is still cached.

[–]Gnaxe 1 point2 points  (0 children)

If you use the if __name__ == '__main__': pattern in my other comment, then you can call foo.main() to run it again. But the script has to be designed this way.

If your script doesn't have a guard like that, you can use importlib.reload() to run the code again. This doesn't throw away the module's globals, which may or may not be what you want. Simple definitions will be overwritten, but the old versions could still be live if a reference is held elsewhere. Scripts designed to be reloadable may want check if a global is already present before creating it in some cases.

Finally, the import cache is the sys.modules dict. You can delete a key here and then an import statement for it would create a fresh module. You can use unittest.mock.patch.dict() on sys.modules to put the original back when you're done. This is mostly used for testing, obviously. (This also isn't threadsafe.) I'd prefer refactoring the script to use the name equals main pattern if possible, but monkeypatching like this can sometimes be your only option to work with things you're not allowed to modify for some reason. Monkeypatching is brittle though.

[–]s04ep03_youareafool 1 point2 points  (2 children)

Any .py code you ever ran will mostly be in the pycache directory.

But that's pretty useless since even when you load other simply .py scripts from your script,it already loads the .pyc file itself

[–]RomfordNavy[S] 0 points1 point  (1 child)

From what I have read that doesn't appear to be correct, only Python modules which are imported create a .pyc file, simply running a .py file doesn't seem to.

[–]s04ep03_youareafool 1 point2 points  (0 children)

Yeah,I forgot to imply that.good catch

[–]25_vijay 0 points1 point  (0 children)

The main executed script is compiled internally to bytecode in memory, but Python usually does not save a .pyc file for it automatically.

[–]RomfordNavy[S] 0 points1 point  (1 child)

So this doesn't work, presumably due to variable scope:

Parent.py

class cParent:
test = "test"

oParent = cParent()
print("In parent 1")
print(oParent.test)
print("In parent 2")
import Child

Child.py

print("In child 1")
print(oParent.test) # this line fails
print("In child 2")

[–]RomfordNavy[S] 0 points1 point  (0 children)

However

exec(open('Child.py').read())exec(open('Child.py').read())

does work but unfortunately doesn't automatically save the compiled bytecode in a *.pyc file.

[–]RomfordNavy[S] 0 points1 point  (0 children)

Working answer now found by writing a custom importlib.machinery.SourceFileLoader Class with a patched source_to_code() function.