Psychopy: Deprecations, useVersion, auto-update, virtualenvs?

Created on 13 Apr 2017  路  16Comments  路  Source: psychopy/psychopy

Adding functions and decorators for deprecations would be helpful.

For functions, this is a start:
https://wiki.python.org/moin/PythonDecoratorLibrary#Smart_deprecation_warnings_.28with_valid_filenames.2C_line_numbers.2C_etc..29

enhancement library

All 16 comments

Something like this may work for keyword arguments:

import warnings


def deprecate(deprecationList):

    def wrap(func):
        def new_func(*args, **kwargs):

            for argument, version in deprecationList:
                if argument in kwargs.keys() and kwargs[argument] is not None:
                    warnings.warn(
                        "Argument {arg} is being deprecated" +
                        "in version {ver}.".format(
                            arg=argument, ver=version),
                        category=DeprecationWarning, stacklevel=2)
            # print(kwargs)
            # print(args)
            return func(*args, **kwargs)
        return new_func
    return wrap


class TestClass():

    @deprecate([('arg3', '2.0')])
    def __init__(self, arg1, arg2, arg3=5):
        print('hello')

Wow, looks interesting!

I'm happy for better ways to document deprecations but not happy (as you know) about quick deprecations for the sake of making code look pretty inside. Lots of users are still using shockingly old versions of PsychoPy for the precise reason that they don't like their experiments breaking, even if they've been warned about it. They don't want to retrieve the study they ran 5 years ago and find that it now won't work and they'll have to work out the range of ways in which psychopy has changed since their code was written.
Breaking backwards compatibility is to be discouraged. It's bad for users and it's bad for reproducible experiments if it means future scientists have to try and install outdated software because new versions don't run the code. We do it when necessary or when a shockingly long time has expired but not just to make code prettier in the short term.

Yes, mostly agreed. Still, we don't have any agreed-upon way to mark code as deprecated (and inform the users about it). The idea here is to decide upon an approach to an orderly deprecation cycle, should a feature become deprecated. I'm unsure how to do it best.

They don't want to retrieve the study they ran 5 years ago and find that it now won't work and they'll have to work out the range of ways in which psychopy has changed since their code was written.

With this part I disagree. If you want to replicate an old study, you should use the exact same version of PsychoPy you ran the original study with. Which is super-easy, since we provide standalone installation packages. We must be able to revert design decisions made 5 years ago should we discover they were suboptimal. Otherwise old and unmaintainable code keeps piling up, and the API doesn't adapt to changed requirements. I am very much in favor of keeping things backward-compatible if we can, but not at any cost.

I agree with "not at any cost" and that shows in PsychoPy history. I'm talking about minimizing. You don't have to keep old copies of other applications and you don't expect your files to become useless. That's something we should be aiming for but I agree we'll fail at times when it's important. That's all I'm saying.

It's really difficult :) We'd also like all users to run the _latest_ version, because there's usually many issues fixed. But if transition is painful (due to incompatibilities), users will refrain from updating their PsychoPy installations... sigh

A crazy idea (i.e. maybe a bad idea): what if we have a dictionary of deprecated functions/arguments and the version at which they were deprecated. Upon calling such a function, psychopy will fail with an error, asking the user to try useVersion('that_version') or even doing it automatically. Running 5+ year old experiments, I don't think people would mind that minimal effort.

It doesn't catch everything, e.g. that visual.ShapeStim.vertices += 1 stopped working when tesselation was introduced, but it would capture everything that can issue a DeprecationWarning.

I think having advice on what version to use to keep the old methods is a great idea, but automatically switching versions would be very confusing if people try to use new functions! But telling people that they can switch back with useVersion() is great.

(I'm currently quite unresponsive as I'm on vacation)

How well does useVersion() work, anyway? The ecosystem around PsychoPy is constantly evolving, whereas useVersion() "only" cares about PsychoPy itself, if I'm not mistaken. But is old code (including an old PsychoPy) really going to run smoothly together with idk, recent versions of pandas, scipy, numpy, pyserial etc.? I highly doubt it. Therefore we should maybe even think about providing a list all the packages (and respective versions) that were bundled with the standalone version of each particular PsychoPy release, and install them into a virtual environment etc. as useVersion() is invoked.

You're right that useVersion only updates the PsychoPy lib. Most of our key dependencies are now fairly stable so that issue isn't awful but yes, ultimately, it's an issue. On the plus side though the existing system is very small - it only requires the psychopy/versions git repository which is 10Mb or so. I'm guessing that packaging virtual envs will be big to deploy. useVersion can also do fancy shit like updating itself: useVersion can use a version from the "future" that the user never installed by doing a quick fetch on the repository! So it works pretty well, but with a limited range.

The big issue, that I don't know how to solve is that it doesn't update the app while the user has it open. An experiment in Builder has to generate its script before running it. The useVersion doesn't currently update all the Builder Components to get their correct version of code outputs.

In short, useVersion works pretty well for scripts in most use cases, but will probably fall over quickly for Builder experiments, unless we do a lot of clever 'reload' commands.

@peircej, Could useVersion somehow detect if Builder is running and then, if changing versions, prompt the user with something like

"This experiment requests that version 1.XX of Builder is used. Builder needs to restart for this effect to take place."
1 Cancel
2 Restart and use version 1.XX once
3 Restart and use version 1.XX permanently

Option #2 is for users who work on multiple experiments and don't want to bother changing version back to the newest. Option #3 is for users who work more focused on this experiment only and don't want to bother keep changing to the requested version.

@peircej

I'm guessing that packaging virtual envs will be _big_ to deploy.

I imagine it such that we simply create a list of packages that ship with each standalone release (e.g., via pip freeze) -- such a list should exist anyway, even if we don't go with virtualenvs. PsychoPy's useVersion() would create a virtualenv and install all those dependencies. For the stuff that's not easily installable via pip, we could probably just copy over the files from the existing install. On my Mac, the Python library folder inside PsychoPy.appis less than 500 MB in size. One could easily install dozens or hundreds of them in in parallel on modern computers, so I don't see a storage issue here.

The big issue, that I don't know how to solve is that it doesn't update the app while the user has it open. An experiment in Builder has to generate its script before running it. The useVersion doesn't currently update all the Builder Components to get their correct version of code outputs.

Could also be solved by using a virtualenv, I guess. We'd simply spawn a new Python interpreter (the one installed into the virtualenv), which then runs the app with the correct version.

Using virtiualenvs would also make package installation much easier for users, who could then very conveniently use pip from within the virtualenv in cases like #1405.

useVersion can use a version from the "future" that the user never installed by doing a quick fetch on the repository!

... which may cause problems if the rest of the distribution is not being update as well :) And the same applies, if I'm not mistaken, to the generic update mechanism in PsychoPy as well -- I do remember some user reports on issues caused by this. So maybe we should revamp the entire thing anyway?

We'd simply spawn a new Python interpreter (the one installed into the virtualenv), which then runs the app with the correct version.

Oh actually you're right. We should do this from a separate process. It still doesn't require a virtual env though. What we need is a callable script psyexp2py that we run from a separate process, where we give it the appropriate ppVersion, psyexpFile, pyFile. it doesn't need to spawn a new app; it just needs to import enough of PsychoPy (specifically, the builder component classes) to compile the script. This will totally work using all the current mechanics :-)

(sorry, I've gone a bit off the track of the issue title!)

(sorry, I've gone a bit off the track of the issue title!)

I personally very much appreciate discussions like that, no need to apologize (at least not from my side!)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

hoechenberger picture hoechenberger  路  10Comments

tpetric7 picture tpetric7  路  7Comments

tiborauer picture tiborauer  路  7Comments

luketudge picture luketudge  路  14Comments

alpinho picture alpinho  路  12Comments