omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular
    1. Home
    2. dgelessus
    3. Best

    Welcome!

    This is the community forum for my apps Pythonista and Editorial.

    For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.


    • Profile
    • Following 0
    • Followers 9
    • Topics 22
    • Posts 1145
    • Best 289
    • Controversial 0
    • Groups 0

    Best posts made by dgelessus

    • RE: Pythonista itself - how was it developed?

      Well-written C code is easy to compile for different architectures, so the fact that iOS devices use ARM instead of x86 processors isn't a huge problem. CPython supports some ARM platforms natively (mainly Linux), so all of the processor-specific problems have been solved already. iOS is also Unix-based and relatively similar to macOS, so even though CPython doesn't support it natively, the differences aren't huge. There are also projects like https://github.com/pybee/Python-Apple-support, which is basically a set of patches to get CPython working properly on iOS/watchOS/tvOS. (I believe this isn't exactly what Pythonista uses - @omz probably has his own custom version of CPython.)

      Interacting between C and Objective-C is also very easy, since Objective-C is more or less an extension of C. This works both ways - you can easily call C code from an Objective-C method, but you can also write regular C functions in Objective-C code, which can use Objective-C features in their implementation, but can also be called from regular C code.

      Many of Pythonista's custom modules (like ui, scene, console, etc.) are implemented using CPython's C API and extension interface, which allows writing Python modules in C. So in principle it's not too difficult to expose iOS Objective-C APIs to Python - you only need to write an extension module in C with some Python-exposed functions that call the appropriate Objective-C APIs. Things get more difficult of course when you also want to provide a nice Pythonic API on the Python side - that requires defining Python classes in C code and converting between Objective-C, C and Python data types where appropriate.

      Another way to interact between Python and C is using the ctypes module, which is part of the standard library, and lets you use (almost) any C API from Python at runtime, without having to compile custom C code. This can also be used to interact with Objective-C APIs, because Objective-C's runtime support library provides a pure C interface to most parts of the Objective-C language. Based on this, @omz wrote Pythonista's objc_util library, which lets you interact with Objective-C APIs and objects from Python. Some of Pythonista's modules (like editor) are implemented in pure Python using objc_util instead of using CPython's extension API. (There is also a non-Pythonista-specific library called Rubicon, which has similar goals and features as Pythonista's objc_util.)

      posted in Pythonista
      dgelessus
      dgelessus
    • RE: Update your Dropbox Python Core SDK

      @DNSGeek It would also mean that Pythonista would not be allowed into the App Store, because of Apple's rules against downloading of executable code. Pythonista had to stop being an "Open In" target already for that reason.

      posted in Pythonista
      dgelessus
      dgelessus
    • RE: Wish list for next release

      Speaking of forum-friendly, a way to view and copy a traceback in plain text form. The debugger is nice, but it cuts off long error messages and there's no way to copy the traceback (AFAICT).

      posted in Pythonista
      dgelessus
      dgelessus
    • Bits and pieces for pythonista_startup

      https://github.com/dgelessus/pythonista_startup

      I'm a big fan of the pythonista_startup feature (thanks @omz!) and use it a lot. Mine grew to a package with six submodules (is this normal?) so I decided to put it up on GitHub.

      Installation is simple - back up your existing pythonista_startup file/folder, then run these commands in stash:

      mkdir site-packages/pythonista_startup
      cd site-packages/pythonista_startup
      git clone https://github.com/dgelessus/pythonista_startup.git
      

      And to update:

      cd site-packages/pythonista_startup
      git pull
      

      Features:

      Almost all of them are turned off by default as not everyone will find them useful or because they're unstable. To turn them on, edit __init__.py and uncomment the entries that you want to have enabled.

      • Anti-globals-clearing mechanism as originally posted here. (Optional)
      • Custom sys.displayhook and sys.excepthook for fancy colors in the interactive prompt, an IPython-like Out history, and cleaner exception display (only for code run interactively). (Optional, colors unsuitable for dark themes)
      • Makes "hidden" built-in types (like function, instancemethod and code) accessible as globals. (Optional, possibly not very useful)
      • Enables the standard faulthandler module to record a Python traceback when Pythonista hard-crashes. (Optional, Python 3 only, slightly untested)
      • Patches for the sys streams to make some "normal terminal" scripts work. (Optional, enabled by default)
      • Proper exception handling for any code run in pythonista_startup and the submodules - by default Pythonista just ignores all exceptions that happen there.
      • Experimental "preflight hook" support to run code every time a script is run from the Pythonista editor. (Optional, very unstable, probably breaks with every Pythonista update)

      It is possible to add custom features in their own subfiles. Each submodule contains a run function, which is run by the main __init__.py. Submodules are not detected automatically, you need to add new ones to the __init__.py by hand.

      posted in Pythonista
      dgelessus
      dgelessus
    • RE: How to ask a good question

      Nicely written @zrzka!

      For image uploads I would suggest Imgur (https://imgur.com/), since it provides all essential image hosting features for free without requiring a login - you can even delete images uploaded anonymously (you get a special link for that after the upload).

      Although using Dropbox works, I don't think it's a very good place to host images. Once you use Dropbox to host an image, you have to keep it around in your files, or the link will break. Even if it's "just" for a support thread, broken images/links are a pain for people who read the support thread later because they have a similar issue. Also, on iOS the Dropbox app has this "feature" where any Dropbox link is opened in the app rather than the browser. This isn't a problem for embedded images, but with regular links it's quite annoying.

      About posting the complete code with which the problem happens - I agree that you shouldn't leave out important info, but posting the entire code isn't always good either, especially when it's a lot of code across multiple files. If possible, try to narrow down where in your code the problem happens, and post only the code needed to reproduce the problem - this is called a minimal, complete, and verifiable example. Doing this makes it much easier for others to help, and in the process you might even figure out the problem on your own.

      One other thing... we all agree that this person that you're linking to wasn't very nice, but I think they got the message. There's no need to keep bashing them, it's not going to make them a better person.

      posted in Pythonista
      dgelessus
      dgelessus
    • RE: Input Issue with Python

      Which Python version are you using to run your script, 2 or 3? This issue could because you're using Python 2 to run a script meant for Python 3. (In particular, the input function does different things in Python 2 and 3, which can cause some obscure problems.)

      In general, if you're encountering a problem when running a specific piece of code, it's always a good idea to include the code in question (as long as it doesn't contain anything secret of course). If you get an error message, copy and paste it into the post as well. (If the error pops up in a red window like in your screenshot, tap on "Print Traceback" to print the error to the console in a copyable format.) For code and error messages, use code blocks (with triple backticks) as @zrzka described above. Without the code and the full error message, it's often difficult to find out what the problem is.

      By the way, almost all of us on the forum here are users like you. The app is developed by a single person, everyone else on the forum is posting in their free time and isn't getting paid to provide support. Please keep that in mind when asking for help.

      posted in Pythonista
      dgelessus
      dgelessus
    • RE: How to upload file to Pythonista?

      To import a file into Pythonista, you can "share"/"open in" it from another app. In the share sheet, select the "Run Pythonista Script" action in the bottom row. (If you don't see it, you might have it disabled. To fix that, scroll all the way to the right, tap on "More", and enable Pythonista in that list.) Now you should get a small Pythonista window with a few icons. Tap on "Import File", this will copy the file into Pythonista.

      Note that you cannot import Python scripts this way (because of Apple's restrictions on importing code). If you want to import Python code from somewhere else, you can copy and paste it into Pythonista, or put it in a zip file and import that.

      posted in Pythonista
      dgelessus
      dgelessus
    • RE: Help - I corrupted my Pythonista somehow

      @Phuket2 Do you have an enum.py module or enum folder in your site-packages or site-packages-3? enum.IntFlag (the class that's missing and causing the exception) is new in Python 3.6, so if you manually installed a different version of the enum module previously, you need to delete it or update it.

      posted in Pythonista
      dgelessus
      dgelessus
    • RE: UI proxies for subclassing

      Please, I really doubt that omz is actively trying to stop you from subclassing certain classes. Most likely these things are just a consqeucene of how Python's C API works. First of all, subclassing needs to be explicitly enabled on the C side. When defining a Python class in C, you basically create a big PyTypeObject (which is a struct) containing all info about the class. One of the struct fields is tp_flags, which contains various flags (duh). For example the one from PyFloat_Type looks like this:

      Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
      

      And for comparison, the one from PySlice_Type:

      Py_TPFLAGS_DEFAULT,                         /* tp_flags */
      

      Note how the one for float has Py_TPFLAGS_BASETYPE while the one for slice does not. And if you try it in Python, you'll see that you can subclass float, but for slice Python will tell you that it's "not an acceptable base type".

      And why not just enable subclassing by default? Well, the C implementation needs to make a special case for when an instance of a subclass is created, rather than an instance of the class itself. In float_new this looks like this:

          if (type != &PyFloat_Type)
              return float_subtype_new(type, args, kwds); /* Wimp out */
      

      In this case the subclass handling is really simple, but that is because float on the C side is mostly a struct containing a double. (And if you check slice_new, there is no speical case for subclasses there.)

      As for the subclass checking, the equivalent of isinstance is implemented for each type by macros like these:

      #define PyFloat_Check(op) PyObject_TypeCheck(op, &PyFloat_Type)
      #define PyFloat_CheckExact(op) (Py_TYPE(op) == &PyFloat_Type)
      

      And PyObject_TypeCheck is also a macro, defined as:

      #define PyObject_TypeCheck(ob, tp) \
          (Py_TYPE(ob) == (tp) || PyType_IsSubtype(Py_TYPE(ob), (tp)))
      

      And PyType_IsSubtype basically returns tp in type(ob).__mro__, which obviously does not call any of __instancecheck__, __subclasscheck__ and __subclasshook__.

      And even if ui's functions accepted things that didn't actually subclass ui.View, it would probably crash the app. The reason is that on the C side, the pointer to the underlying UIView or controller or whatever is actually a struct field, and _objc_ptr is likely a read-only descriptor returning that field's value. So no, _objc_ptr is not sufficient, that's only a convenience attribute for objc_util.

      posted in Pythonista
      dgelessus
      dgelessus
    • RE: ctypes pythonapi version

      @ywangd That's a unicode/str/bytes issue. Short answer, the arguments to Python's C API need to be byte strings (b"...") unless stated otherwise in the docs. Long explanation below. :)

      In C, the type char represents a byte (which is generally agreed to be 8 bits nowadays). Most code uses char * (a pointer to a char, which is effectively used as an array of unknown size) as the data type for "strings". Because a char is only 8 bit wide, it can't hold a full Unicode code point. There is the wchar_t data type, which is not really standardized either, but it's wider than char and can usually hold a Unicode code point, so APIs that support Unicode properly use wchar_t * instead of char * for strings.

      In Python 2, the situation is similar. str is like C's char * - it's made of 8-bit bytes and can't hold Unicode text properly, and unicode is like C's wchar_t * and supports full Unicode. That's why ctypes converts str to char * and unicode to wchar_t * and vice versa.

      Now Python 3 comes along and cleans up a lot of Python 2's Unicode issues. In Python 3, you have the two data types bytes and str. Python 3's bytes is an 8-bit string like Python 2's str, and Python 3's str is a Unicode string like Python 2's unicode. And most importantly, in both Python versions the string "hello" is of type str, which means that under Python 2 it's 8-bit (i. e. char *) and under Python 3 it's Unicode (i. e. wchar_t *).

      Python's C API functions, such as PyRun_SimpleString use normal char * for source code. So under Python 2, your code works fine - "print(42)" is an 8-bit string, gets converted to char *, which is what PyRun_SimpleString wants. Perfect. Under Python 3, "print(42)" is a Unicode string, which gets converted to wchar_t *, and then things go wrong. Because wchar_t is 32 bits wide under iOS, the text print(42) represented as a wchar_t * has three null bytes between each character (which would be used if the character had a higher code point in Unicode). Null bytes are also the "end of string" marker in C. Python reads the start of the wchar_t * string, but expects a char * - it sees a p, then a null byte, and thinks "great, I'm done" and so it just runs p instead of print(42).

      posted in Pythonista
      dgelessus
      dgelessus
    • RE: Pythonista-Tools is now open for collaboration -- Please join the effort

      @ccc said:

      Hey folks... https://github.com/Pythonista-Tools now has 255 stars on GitHub

      "Hello, this is GitHub support. Be advised that your repository Pythonista-Tools may be unreachable during the next few hours while we allocate a second byte for your star count. We hope to resume normal operations shortly and apologize for any inconvenience caused. Happy coding!"

      posted in Pythonista
      dgelessus
      dgelessus
    • Python console in the Today widget

      The Today widget is no longer included in the release version of Pythonista! You cannot run this script in the notification center anymore!

      If you still want to try it, you can download and run it normally in Pythonista. The UI will appear as a normal sheet view instead. This script is not very useful outside of the notification center though.


      https://github.com/dgelessus/pythonista-scripts/blob/master/today_python_console.py

      It had to be done. Not very useful and not very polished, but a fun exercise. The keyboard is just a bunch of ui.Buttons, and the "text field" is a ui.Label. There is a cursor, but it needs to be moved with arrow "keys".

      To use this script meaningfully, you need to have Pythonista 3 and need to set this script as the Today Widget script. It is possible to run this script normally from the editor, but this meant for debugging - doing so is not very useful on its own and probably breaks the interactive console (until you restart the app).

      (Warning: Very likely to crash if another app or too many widgets are running. Works best on the home screen.)

      posted in Pythonista
      dgelessus
      dgelessus
    • RE: string.maketrans() method not working

      Exactly. str is the string class, and you can use it to convert an object to a string. For example, str(42) is the same as '42'. This is important when you want to do something like this:

      print("Your score is: " + str(42))
      

      Without using str, you would get a TypeError, because you can't add a string and a number.

      This means that if you assign something different to the name str, you cannot use the original str class anymore. The same goes for other standard types like int, float, list, dict, etc.

      posted in Pythonista
      dgelessus
      dgelessus
    • RE: Trouble getting started with Pythonista

      @paulgottlieb Python 3 is generally the better choice, if you have the choice. Sometimes you have to use Python 2 because you're using some other component that only supports Python 2, such as Pythonista in this case.

      But if you do have the choice, I strongly recommend Python 3, as it has far less special cases that exist only for backwards compatibility, like print being a special statement instead of a function, input/raw_input, bytes/str/unicode conversion, old-style and new-style classes, etc. If you're going to work with Python 2, you'll need to learn about these things at some point, but if you're just starting with Python I'd recommend Python 3 - it's a lot cleaner and easier to understand in a few cases.

      The current version of Pythonista only supports Python 2, but a version with both Python 2 and 3 is currently in beta (if you're interested in testing it, see the forum thread on the topic). As far as I know it will be released as a separate app once it's finished.

      posted in Pythonista
      dgelessus
      dgelessus
    • RE: More keyboard shortcuts

      Cmd+1 through Cmd+0 for switching between tabs (editor and console) would be nice too. Discovered that feature once by accident in Firefox, now it's the only way I switch between tabs.

      posted in Pythonista
      dgelessus
      dgelessus
    • RE: Bug list for beta release 201001

      @Webmaster4o Hint: use os.symlink.

      posted in Pythonista
      dgelessus
      dgelessus
    • RE: Is there an equivalent to objc_util, but for Swift?

      I'm also not aware of any existing project that allows calling Swift from Python. As others have said above, the other direction (calling Python from Swift) is quite well-supported, but that doesn't help us much here.

      In principle, it is entirely possible to call Swift from Python. It would require more work than with Objective-C though.

      Objective-C's calling conventions are completely compatible with C, which allows using the standard ctypes library to call Objective-C code. The Objective-C API also exposes a lot of information at runtime, which allows inferring method return/argument types so you don't have to set them manually in most cases.

      With Swift the situation is quite different. As far as I know, Swift itself does not provide nearly as many runtime API functions as Objective-C does, so there is no easy way to get function/method type information at runtime. Swift might not even store this type information in the compiled binaries at all (I haven't checked recently).

      There's also the more fundamental problem that Swift's type system and calling convention are completely different from C. In some simple cases the calling conventions overlap enough that you can call some Swift functions using ctypes, but IIRC all instance methods (anything with a self) and functions/methods with certain complex return types (tuples, etc.) are incompatible with the C calling convention.

      This means that calling Swift from Python would require a custom extension module similar to ctypes, but for the Swift calling convention. Internally, ctypes uses the libffi library, but libffi does not support the Swift ABI (yet?). In any case, since a custom native module is required, a library like this wouldn't be usable in Pythonista unless @omz compiles and includes it in the app.

      TLDR: It would be possible, but would require quite a bit of work, and couldn't be developed in Pythonista.

      posted in Pythonista
      dgelessus
      dgelessus
    • RE: how can i access dispatch_get_main_queue

      dispatch_get_main_queue is not a normal C function, so you can't access it normally via ctypes/objc_util. Instead, you need to reproduce dispatch_get_main_queue's functionality in Python using some custom code:

      from ctypes import Structure, byref, cast, c_void_p
      from objc_util import ObjCInstance, c
      
      class struct_dispatch_queue_s(Structure):
          pass
      
      _dispatch_main_q = struct_dispatch_queue_s.in_dll(c, "_dispatch_main_q")
      
      def dispatch_get_main_queue():
          return ObjCInstance(cast(byref(_dispatch_main_q), c_void_p))
      

      You don't need to understand what this code does exactly - you can just copy-paste it into your code and use dispatch_get_main_queue() like you would normally.

      (If anyone is interested in the exact details of what the above code does and where it comes from, see this page. The original C code is very complex, so you need to be familiar with both ctypes and the C preprocessor to understand all the translation steps. But again, you don't have to understand all of this - you can just copy-paste and use the finished Python code.)

      posted in Pythonista
      dgelessus
      dgelessus
    • RE: Help finding the value of an objc string constant...

      @shinyformica Correct - these string constants are actually global variables of type NSString *, which means that they contain a pointer to the actual string object.

      To read the pointer from the global variable, you need to use ctypes.c_void_p.in_dll(objc_util.c, "ConstantName"), as @mikael already found above. objc_util.c.ConstantName will "work" (it doesn't crash), but that treats ConstantName as a function and not a variable, so it returns a function pointer (which will crash when you try to call it, because normal variables are not valid functions).

      The value returned by in_dll is a regular Objective-C object pointer (which points to the NSString object), but as a c_void_p object, so you need to cast it to ObjCInstance manually so that you can use it. Once you do that, you have a regular NSString object with the value you're looking for.

      In short:

      >>> objc_util.ObjCInstance(ctypes.c_void_p.in_dll(objc_util.c, 'UICollectionElementKindSectionHeader'))
      <b'__NSCFConstantString': UICollectionElementKindSectionHeader>
      >>> objc_util.ObjCInstance(ctypes.c_void_p.in_dll(objc_util.c, 'UICollectionElementKindSectionFooter'))
      <b'__NSCFConstantString': UICollectionElementKindSectionFooter>
      

      Strangely, the value of both string constants is literally their name, so the approach suggested by @mikael (just pass the name of the constant as a string) should have worked. Maybe UIKit expects you to pass in the exact pointer, and not just any string with the same contents? (That would be quite strange though.)

      (By the way, please do @-mention me if there's something where you think I can help. Sometimes I only skim the forums very quickly, so I might miss some questions by accident.)

      posted in Pythonista
      dgelessus
      dgelessus
    • RE: About using CFFI in Pythonista (and my CFFI-based libraries)

      There are also a couple of CFFI-based libraries that I wrote, which have been linked in the other post. It seems that I never made a proper post on the forums about them, so I'll do that here:

      The first one is cffipp (found in my pythonista-c-utils repo), which adds a proper C preprocessor to CFFI. By default CFFI's C parsing understands almost no C preprocessing features (only simple constant #defines are supported). This library adds support for all important C preprocessor directives and features, which makes it possible to load standard iOS headers into CFFI (as long as they are pure C, such as Core Foundation). The repo includes patched versions of a few headers, to make them compatible with CFFI and improve parsing performance.

      Unfortunately, cffipp is now quite outdated - I wrote it three years ago based on CFFI 1.6 and the headers for iOS 9. The CFFI and header patches include a lot of copied code, so updating it to newer CFFI and iOS SDK versions would take a bit of work. If anyone is interested in using this, let me know, I might be able to get it updated.

      The second library is objc-cffi, which is basically a CFFI-based version of objc_util with a few extra convenience features. This library is fairly incomplete - although it has a few features that objc_util doesn't (introspection, better automatic type detection for methods) it's missing some very important features for practical use (class definitions, block support, collection syntax).

      I also found out quite soon that CFFI is not well suited as the base for this library. To interact with the Objective-C runtime I need to create a lot of types dynamically, which is very slow with CFFI, because every type needs to be constructed as C source code and then parsed by CFFI. I also ran into name collision issues, because every FFI object has a single namespace and does not allow multiple definitions (the Objective-C runtime sometimes returns different type information for the same type, depending on context).

      Around this time I also started contributing to rubicon-objc, which has also been mentioned a few times on this forum - it's basically objc_util, but not Pythonista-specific (also works on Mac and other platforms with Objective-C). I've ported most of objc-cffi's convenience features over to rubicon-objc now, and I'm still working on improving rubicon-objc, so I would definitely recommend that over objc-cffi.

      posted in Pythonista
      dgelessus
      dgelessus