• zrzka

    @mikael simple answer, pretty busy these days :/

    posted in Pythonista read more
  • zrzka

    Black Mamba 1.4.1 released:

    • Compatibility check with 3.1.1 (311016)
    • Bundle refactoring enhanced with future, libfuturize, libpasteurize modules
    • script/refactoring/futurize.py introduced (see Python Future)
      • Equivalent of futurize -1 --all-imports -n -w --add-suffix 3 $editor.get_path() (Stage 1 only)
      • When futurizer ends, editor text is replaced with content of the .py3 and .py3 is trashed
      • You can run futurizer script with Cmd Option F
    • Improved updates check
      • Console is not cluttered with local / latest release info (installer prints this)
      • Update check() doesn't ask if update should (not) be installer (installer also asks)
      • If there's new update available, installer is executed, you will still be asked (just once, not twice)
    • Script new_file.py modified
      • File opened
        • Asks for a file name (empty & Enter -> Cancel)
        • New file path is currently opened file dirname + entered file name
        • If file doesn't exist, new file is created and opened
        • If file exists, file is opened
      • No file opened
        • Same behavior as now
        • New tab created and New File... button tap emulated
    • Installer
      • Replaced ModuleNotFoundError (Python 3.6) with ImportError (Python 3.5)

    posted in Pythonista read more
  • zrzka

    I'm rewriting the Apple demo (candles, ...) into Python(ista). Will share it when finished.

    posted in Pythonista read more
  • zrzka

    Nope, the key is not required. See https://developer.apple.com/documentation/arkit (Important section at the beginning). You only need device with >= A9 chip. Use isSupported to check if your device is supported.

    posted in Pythonista read more
  • zrzka

    According to Ole's reply in one issue. @on_main_thread is there for objc_util only. Every other module should handle this internally. If not and main thread is required, it's a bug.

    See https://github.com/omz/Pythonista-Issues/issues/461 for more info.

    posted in Pythonista read more
  • zrzka

    Black Mamba 1.4.0 released:

    • Fixed keyboard shortcut selector name generator
    • Bundle refactoring introduced (includes rope)
    • Refactoring functions introduced
      • It's an EXPERIMENT. You should use version control system to avoid loosing data.
      • Cmd Option O - Organize imports
      • Cmd Option E - Expand star imports
      • Cmd Option R - Rename identifier
      • Can be used as scripts as well, see script/refactoring folder
      • Preview dialog can be closed with Cmd . / Esc, confirmed with Enter
    • Scroll back to initial cursor location if analyzer didn't find an issue, ugly, but better than end of the file

    NOTE: As mentioned, refactoring is an experiment. It does use rope internally, but it seems that the rope is unable to cope with built-ins, etc. (in case of expand star imports). Rename and organize imports works. Here's the screenshot if you'd like to check it. Use with caution.

    posted in Pythonista read more
  • zrzka

    @Phuket2 thanks for the idea. It's already reported as #21, but I'm not still sure if I should do this. Basically I don't need it, I'm happy with jump to definition, find usages and other stuff. Full text search can be slow, because you have to check all files again and again, or you have to store some indexes, then you have to validate them, check if something were changed, ... I'm not going to do this probably. Sorry :) As I wrote, focusing on refactoring, that's what I miss now.

    posted in Pythonista read more
  • zrzka

    Sneak peak of new features that are coming ...

    ... 1.4 will be focused on refactoring. Another feature will be import organization (sorting, ...).

    posted in Pythonista read more
  • zrzka

    Cleanup

    Theory

    Context manager

    One way to clean up resources is context manager. Here's an example of class based context manager.

    class File:
        def __init__(self, path, mode = 'r'):
            self.path = path
            self.mode = mode
            self.handle = None
    
        def __enter__(self):
            self.handle = open(self.path, self.mode)
            return self.handle
    
        def __exit__(self, *args):
            self.handle.close()
                
    
    with File(__file__) as f:
        # __file__ - contains script name, basically opens itself
        # __enter__ was called, file is opened and handle is stored in f (== File.handle)
        print(f.read())
        # __exit__ will be called
    

    Shorter way to achieve same thing is contextmanager decorator. Example:

    from contextlib import contextmanager
    
    @contextmanager
    def file(path, mode = 'r'):
        handle = open(path, mode)
        yield handle  # yields handle -> as f and the nested with block is executed
        handle.close()
    
    with file(__file__) as f:
        print(f.read())
    

    There's also contextlib.ContextDecorator, which allows you to use it as a function decorator or you can use it with with statement. If you don't know yield, search for Python generator for example. Head over to the contextlib documentation to learn more. Also check contextlib.closing if the only thing you would like to do is to call close().

    try / ... / finally

    One way is to use finally:

    handle = None
    try:
        handle = open(__file__, 'r')
        print(handle.read())
    finally:
        if handle:
            handle.close()
    

    Hmm, what if we do not want to catch handle.read() exception for example? else is our friend:

    handle = None
    try:
        handle = open(__file__, 'r')
    except OSError:
        # Executed if try: raises OSError
        print('Failed to open file')
    else:
        # Executed only and only if try: doesn't raise exception
        print(handle.read())
    finally:
        # Always executed
        if handle:
            handle.close()
    

    Read Defining Clean-up Actions to learn more.

    finalizer

    Another way to clean up is to use weakref.finalize. Here's an example:

    class TinyDBWrapper:
        def __init__(self, path):
            self._db = TinyDB(path)
            self._finalizer = weakref.finalize(self, self._db.close)
    

    Finalizer is fired when:

    • object (TinyDBWrapper in this case) is garbage collected,
    • program exits.

    What if you'd like to provide close method too? You can add it in this way:

    class TinyDBWrapper:
        def __init__(self, path):
            self._db = TinyDB(path)
            self._finalizer = weakref.finalize(self, self._db.close)
            
        def close(self):
            self._finalizer()
    

    And now finalizer is fired when:

    • object (TinyDBWrapper in this case) is garbage collected,
    • TinyDBWrapper.close is called,
    • program exits.

    del

    And here comes the never ending story of del, __del__, ... What's the problem with __del__?

    • You're responsible for calling any __del__ in superclass
    • Exceptions raised in __del__ are ignored
    • __del__ is called when GC collects objects and not when the last reference is lost
    • Prior Python 3.4, __del__ prevents collection of cyclic links (see PEP442), even >= 3.4 has still some issues though
    • It's not guaranteed that __del__ is called when the interpreter exits.

    Head over to object.del(self) to learn more.

    What's the problem with del?

    People think that del is equivalent of GC - collect me now - but it isn't. del removes binding and decreases reference counter. That's all what it does. When the object is GCed? It's implementation specific and it depends on GC scheme. CPython collects it when reference counter reaches zero. But other implementations can do it later.

    You want to rely on this? DB wrapper where you have no idea when the close will be called (if ever). Data loss? Here's my rule of thumb - hard to explain? Bad idea.

    Practically

    Simplicity

    And lets say I wanted to share that as a module for others to use. This is when I started looking at this clean up issue again. I thought, maybe that I am a little further on I might be about to find a way to do it. Rather than telling the user, you must call close otherwise your data may not get written to disk or the file might get corrupted.

    To make it short - you're trying to be too smart with your module. What you should do is to provide simple API, which allows to:

    • explicitly open catalog (can be __init__),
    • explicitly close catalog (like close method),
    • use your wrapper as context manager with with.

    And leave the rest on the developer. You have no idea how your module will be used, when it will be used, if it's really required to keep the wrapper around for the whole lifecycle of the application, ...

    If you'd like to help, add finalizer if consumer forgets to call it, but I wouldn't do it, close is enough. This is not kindergarten :)

    will_close

    @mikael asked about will_close. Don't use will_close for stuff like closing streams, databases, ... This should be done on lower level. What if will_close is not going to be called? It's like depending on __del__ and you don't know when and if it will be called.

    How to check if finalizer was called

    #!python3
    
    import weakref
    from tinydb import TinyDB
    
    
    class TinyDBWrapper:
        def __init__(self, path):
            self._db = TinyDB(path)
            self._finalizer = weakref.finalize(self, self._db.close)
    
        def close(self):
            self._finalizer()
    
    
    # Instantiate wrapper
    wrapper = TinyDBWrapper('aa.json')
    # Cheat, get reference to finalizer
    fin = wrapper._finalizer
    # Trash the wrapper
    wrapper = None  # or replace with `del wrapper`
    # Get finalizer status, if alive is False, finalizer was already called
    print(fin.alive)  # prints False, because of CPython, ref count == 0, ... can differ on desktop / other implementations ...
    

    Conclusion

    Yeah, I know, I'm too pedantic sometimes. But simple stuff works, doesn't require you to write long documentation, explanation, ... And it's good. Remember my rule of thumb? Hard to explain? Then it's a bad idea.

    Just provide a way how to do it explicitly and leave the rest on the developer. He has lot of ways (context manager, ...) how to handle this and only he knows how / when your module is going to be used. You don't know it. And you can't prepare your module for all possible use case scenarios.

    All these examples were written in Python 3 and tested in Pythonista 311015 (latest beta).

    posted in General Discussion read more
  • zrzka

    See this comment. Quote:

    Yeah, I’m aware that I should support this eventually, but I’d like to do it in a somewhat consistent manner, not just in some random part of the app. That’s quite a bit of work, as I’d have to look at pretty much every piece of UI, and it’s unlikely that I’ll get to it for this release.

    support this = Dynamic Type. Will probably be added, but not this release, maybe not even in the next release, ... It's quite a big amount of work to support this everywhere.

    posted in Pythonista read more

Internal error.

Oops! Looks like something went wrong!