Basic features don’t work with iOS 11
Pythonista is great! Thanks for producing it. Unfortunately since iOS11 came out my iPad Air is having a lot of difficulties. Here are two that I’ve found with Pythonista 3.
Smart Quotes: The keyboard produces smart quotes (single or double) when using a triple-quote for a comment. Can probably change the default keyboard or suppress this, but haven’t looked for it yet. Fortunately, the character bar quotes work!
Importing modules/files. Pythonista can no longer find the cwd from the console. Using os.listdir(‘.’) will show the file, but you can’t import it. You also can no longer run the file, switch to console and use the defined variables and classes as was possible previously. Things do work if you create and run a file that imports a second file. Just wanted to see if others are having these issues?
Fixed the smart quotes by turning off "Smart Punctuation" under Settings - General - Keyboard.
Not sure if you used pythonista 3 before ios 11, but...
In many recent versions of pythonista,
'.'is not on sys.path. When you run a script, it adds that folder to sys.path.
One common mistake is if you run a script with a shebang #!python2, but your console interpreter is set to 3.5, the 3.5 interpreter still wont be able to import from the script path.
You should be able to print sys.path to debug what is happening.
@JonB Hi. I am new to python. This behaviour is a change with the new OS. On the iPad all the scripts are in the app's file space. It's reasonable to assume that directory is always on path--at least the top level folder, which is where my script is located.
This is just an annoyance really. Things still work, but extra effort is needed to go test your new code. Other non-Pythonista problems are rampant with iOS 11, like my icons oriented at 90 degrees to my background. A reboot fixed that for now, but it seems turning the iPad at the wrong moment will do that. After a few updates, maybe everything will work again.
When you run a script from the editor, three things happen:
- globals are cleared
- imported modules are cleared
- sys.path is reset, and the current script's abspath is inserted at the top. (sys.path defines which folders are checked when you import a module.)
- os.chdir() to current file's path.
1&2 mean that when you run a script, those scripts cannot depend on anything you have done in the console. 3) means after you run a script, you can import modules in the same path as your script, unless you are actively mangling sys.path in your script.
These actions should be os independant, since they actually happen in python, not natively (a special script called pythonista_preflight.py). Globals should remain in the console's interpreter, which are also a python interpreter, not os thing.
The initial sys.path when you first start pythonista does not include the current path, so you cannot type
import mymodulein the console, unless you either run a script first (which inserts onto sys.path), or manually type
sys.path.insert(0,'.'), or add this line to a pythonista_startup.py in site-packages.
Note there are two, separate interpreters, each with their own sys.path. From your description, I suspect you are running a script that has a #!python2 at the top, but ar using the 3.5 interpreter in the console, (check upper right corner), or you have accidentally changed the console's interpreter from the default.
To check this,
Can you create a script, with the following:
import sys, os print(sys.version) print('cwd:',os.path.abspath('.')) print('sys.path:') print(sys.path) x='i am a global' print(x)
and run it, and paste the results back here?
Then, enter the console, and type:
What you should get is something like:
3.5.1 (default, Sep 20 2016, 14:02:36) [GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.31)] cwd: /private/var/mobile/Containers/Shared/AppGroup/C534C622-2FDA-41F7-AE91-E3AAFE5FFC6B/Pythonista3/Documents sys.path: ['/private/var/mobile/Containers/Shared/AppGroup/C534C622-2FDA-41F7-AE91-E3AAFE5FFC6B/Pythonista3/Documents', '/private/var/mobile/Containers/Shared/AppGroup/C534C622-2FDA-41F7-AE91-E3AAFE5FFC6B/Pythonista3/Documents/site-packages-3', '/private/var/mobile/Containers/Shared/AppGroup/C534C622-2FDA-41F7-AE91-E3AAFE5FFC6B/Pythonista3/Documents/site-packages', '/var/containers/Bundle/Application/2A6A9B41-FA6B-48F8-9F33-B725FA045931/Pythonista3.app/Frameworks/Py3Kit.framework/pylib', '/var/containers/Bundle/Application/2A6A9B41-FA6B-48F8-9F33-B725FA045931/Pythonista3.app/Frameworks/Py3Kit.framework/pylib/site-packages', '/var/containers/Bundle/Application/2A6A9B41-FA6B-48F8-9F33-B725FA045931/Pythonista3.app/Frameworks/Py3Kit.framework/pylib/site-packages/PIL_compat'] i am a global >>> print(x) i am a global
If you get an NameError after typing in the console, then type in the console:
>>> import sys >>> prinr(sys.path) >>> print(sys.version)
And paste those results back here too.
@jonB Hi. I totally agree with what you say. But part of it doesn't work. That part is when I run a script that defines a class and then immediately go to the console and try to use the class. This used to work and has stopped. I could even import other modules in the same directory as the first I ran, but now that doesn't work anymore.
I copied your test script and put it in a file, which I then ran. Here is the result:
3.5.1 (default, Sep 20 2016, 14:05:14)
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.31)]
['/private/var/mobile/Containers/Shared/AppGroup/87C36EF0-5007-410A-81F1-2CDA5EF1C05E/Pythonista3/Documents', '/private/var/mobile/Containers/Shared/AppGroup/87C36EF0-5007-410A-81F1-2CDA5EF1C05E/Pythonista3/Documents/site-packages-3', '/private/var/mobile/Containers/Shared/AppGroup/87C36EF0-5007-410A-81F1-2CDA5EF1C05E/Pythonista3/Documents/site-packages', '/var/containers/Bundle/Application/AAFBB031-7AB0-4862-8A10-4DE7B07D7E10/Pythonista3.app/Frameworks/Py3Kit.framework/pylib', '/var/containers/Bundle/Application/AAFBB031-7AB0-4862-8A10-4DE7B07D7E10/Pythonista3.app/Frameworks/Py3Kit.framework/pylib/site-packages', '/var/containers/Bundle/Application/AAFBB031-7AB0-4862-8A10-4DE7B07D7E10/Pythonista3.app/Frameworks/Py3Kit.framework/pylib/site-packages/PIL_compat']
i am a global
When I then print(x) on the console it works as per your example.
Now for the weird part! I retried my running my class definition. It didn't work. I did it again. It didn't work. Out of desperation I tried import my class--and it worked! I then went a=myclass.myclass(0.,0.) and it works. Just like it used to.
@JonB. So, thanks. I think this is just a flakey part of the new iOS update. Things don't work and then suddenly do. I appreciate your help on this.
When you say you retried running your class, what do you mean that it didnt work? Since mostly the python interpreter environment is independent of the os, it would really be surprising if what you describe is correct.
Are you getting NameErrors, or just the classes are hot popping up in autocomplete? I will note that sometimes autocomplete takes a little bit to get caught up, and sometimes never does.
Classes are nothing more than globals with type of
typeand imported modules are just variables with type
moduleso there really is nothing different between a variable x and a class, or a imported module.
I would also point out that, if you have a class called myclass inside a file myclass.py, and sometimes you run myclass.py, and sometimes you import myclass, it can sometimes get confusing whether you refer to the class as myclass or myclass.myclass! You could resolve this by using from imports (and caps on class names is often the convention), so that you can refer to it both places just as MyClass, eg
from myclass import MyClass
Maybe you could paste an annotated (i.e add comments into the console like, ran myclass.py from editor) console session showing when you get errors, along with sys.path and os.listdir printouts when you get errors. If globals are clearing on their own, or sys.path resetting on its own, that certainly is not desired behaviour and you'd want to make a bug report.
By didn't work, I meant that the console didn't know about the class in the file that was just executed. Suddenly, it did work and remained working all evening. Tonight, I can't import or use the class in the just run file.
So, I checked sys.path as you suggested. Sure enough the top-level directory of Pythonista isn't in the path.
No errors of any kind appear, except no module myclass (not the real name, but it doesn't seem to matter), which since the folder isn't in the path makes sense.
Which is the folder where the file is located.
I think this is a bug. It works sometimes and not others. It began when I updated the OS. Before the update, this worked every time.
does this happen immediately after running a script? or after switching apps? Are you using #!pythonX at the top of your script?
I have three possible theories:
what may be happening is that the interpreter is restarting, because pythonista got restarted due to memory management. That starts you off fresh. You would probably notice that the console is empty.
Or, you are changing the interpreter version accidentally. Try print(sys.version) when you have this problem.
Or, you didn't actually run your script. Sometimes when you press play, the button press doesnt register. Of course, if you have prints or other indication that your script is running, then this would be obvious.
Some script is manipulating sys.path
Here is something you can do: place a file in site-packages, pythonista_startup.py, which contains the following:
import sys print('initializing interpreter') print(sys.version)
that way, it will be clear when the interpreter gets reinitialized, since this will be printed to the console. if you see that, then you know you need to run your main script again.
As a workaround, at least in the v3.5 interpreter, if you like to be able to import from Documents even before running a script, or reset the path whenever it is reset, you can add this to pythonista_startup:
#place into site-packages/pythonista_startup.py, or something called from site-packages/pythonista_startup/__init__.py. class ModuleProxy(object): def __init__(self, module): '''store a copy of sys in module. also set sys.path to include .''' if 'ModuleProxy' in repr(type(module)): module = object.__getattribute__(module, 'module') object.__setattr__(self, 'module', module) if '.' not in module.path: module.path.insert(0,'.') def __getattribute__(self, name): '''call sys.__getattribute__ except for __reset__''' if name=='__reset__': return object.__getattribute__(self, name) module = object.__getattribute__(self, 'module') return getattr(module, name) def __setattr__(self, name, value): '''forces '.' to be at top of path when path gets reset. otherwise call sys.__setattr__''' module = object.__getattribute__(self, 'module') setattr(module, name, value) if name=='path': module.path.insert(0,'.') print('Path was reset. Forcing . to be on top.') def __reset__(self): '''replace old sys''' global sys sys.modules['sys']=object.__getattribute__(self, 'module') sys=sys.modules['sys'] import sys sys.modules['sys']=ModuleProxy(sys) sys=sys.modules['sys']
This only works for thr v3 interpreter, I think the v2 interpreter sets the path in a different way.
I updated to ios 11.02 yesterday and noticed that apps are not being closed at the frequency that they were when I updated to iOS11. I had read about this happening in some news article and had observed it happening. Keep in mind, I have the latest and highest spec iPad Pro 12.9 and it was still happening to me. So your point 1 sounds very realistic.