Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ADD] progress: Decorator for progressbar #233

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

pedrobaeza
Copy link
Member

@pedrobaeza pedrobaeza commented Apr 3, 2021

This is a decorator for any sub function called in an OpenUpgrade script (pre, post or end migration script).

Decorate functions that may take time. If a function is decorated, we provide an iterable argument that will be looped on and call the original function, while a progress bar will be displayed.

Function: 28% (152 of 529) I#####              I Elapsed Time: 0:00:03 ETA: 0:01:32

:param index: Index of the argument to be used as iterable. Default to the second argument. It will pass each of the elements of the iterable in the same place.
:param title: Optional title for prefixing the progress bar. If not specified, the function name will be used.

Typical use:

@openupgrade.progress()
def migrate_some_stuff(env, record)
    # some custom code
    ...

@openupgrade.migrate()
def migrate(env, version):
    records = ...  # get an iterable
    migrate_some_stuff(env, records)

Co-Authored-By: Yann Papouin [email protected] @ypapouin

@Tecnativa TT19674

@pedrobaeza
Copy link
Member Author

See a usage example in OCA/OpenUpgrade@3c4fc81

@yajo @joao-p-marques this means an extra library for openupgradelib. I think we should include progressbar2 in Doodba.

@pedrobaeza

This comment has been minimized.

Copy link
Contributor

@legalsylvain legalsylvain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice addition ! thanks to have worked on that topic !

No tested. (and light code review, because decorators are quite hard to understand for me).

Question : what is the impact, if the log is written in a file ? the progression is written ?

Typical use::

@openupgrade.progress()
def migrate_some_stuff(env, record)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

record -> records.

I mean, should be work with one or many records. (if one record, it should be in an interable).
If not, it will not be possible to add / remove the decorator easely.

This could avoid the big diff, introduced here OCA/OpenUpgrade@3c4fc81

Do you think it's pertinent ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That can't be possible, Sylvain, as this needs to control each step of the loop for the progress bar. Think on this like @api.one decorator, in which self is only one record. You pass the whole recordset, but what gets to your method is only one. Here I took care of putting the proper nomenclature of the arguments for not confusing.

The only way to get what you want is to intercept things into another level, like initially @ypapouin did in his PR #206, but that means that we need an specific decorator for each kind of operation (his one was for a fetchall), and lose the general purpose. We can override the iterator and create a wrapper, but this still is limited, as you can't have several nested loops, so it's not a lot of problem to change this. Now it seems a bit dirty as applied over a method that previously was not "progressified", but when writing new methods, there won't be diff and even the cyclomatic complexity will be lower, reducing one indentation level.

Copy link
Contributor

@legalsylvain legalsylvain Apr 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @pedrobaeza.

thanks for your answer.
In a first sight, I was thinking to replace args_copy[index] = elem by args_copy[index] = [elem]. It can do the job.

  • It's not perfect and should ask some minor changes for dict but it avoid huge diff. (dict.items() can not be used inside and should be replaced)
  • Also, if using progress bar generates side bad effects for any reasons (performance, in the log, or whatever), it could be great to disable / remove it easily)
  • if using the progress bar requires to change the code, it will slow down the use of this new decorator for the current migration scripts.
  • when writing function, it is counter intuitive to call a function with a list, and in the function, to have a single element to consume.

In the other hand,

  • I like the reduction of the complexity you're talking about
  • indeed it will not be a problem for new function

I don't know ! I have mixed feelings on that point. Maybe other openupgrader will will have additional opinions.

Any way, not a blocking point. I think that introducing progressbar is a step in the right direction.

Note : not sure to have been convinced by the @api.one api. Most of the functions call in the first line, the self.ensure_one() command, that is raising an error if a recordset with more than one item is provided.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I sincerely don't think passing a list of one element is the way to go. It's a bit antinatural and only for these cases where we adapt existing methods to reduce the diff, but it's less readable and put in doubt what the decorator does IMO.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when writing function, it is counter intuitive to call a function with a list, and in the function, to have a single element to consume.

This is done in other places like migrate(cr, version) that gets converted to migrate(env, version).

@pedrobaeza
Copy link
Member Author

About the impact in the log, it depends on the log technique you use. In principle, stdout is written as much times as progression steps displayed, so that's not ideal, but I haven't investigated much yet about how to avoid it. There's some info about this in the library itself in https://github.com/WoLpH/python-progressbar#progressbars-with-logging.

There's also other progress bar library mentioned frequently, called tqdm (https://github.com/tqdm/tqdm), and I don't know if it's better than this one.

@legalsylvain
Copy link
Contributor

In principle, stdout is written as much times as progression steps displayed, so that's not ideal, but I haven't investigated much yet about how to avoid it

if the log file is polluted, we could add a test like if not tools.config['logfile'].

@joao-p-marques
Copy link
Member

@yajo @joao-p-marques this means an extra library for openupgradelib. I think we should include progressbar2 in Doodba.

Ok, but I think that, if the dependency is correctly set up in the Python package (i.e. setup.py or requirements.txt, as you do here), we shouldn't need to change anything.
When installing openupgradelib downstream (in doodba-copier-template, for example: https://github.com/Tecnativa/doodba-copier-template/blob/stable/odoo/custom/dependencies/pip.txt#L1) it should be installed by default.
@yajo can you confirm please?

@pedrobaeza
Copy link
Member Author

if the log file is polluted, we could add a test like if not tools.config['logfile'].

@legalsylvain the logfile is not polluted. I meant that if you do -u all and redirect stdout to a file, then progressbar is logged.

This is a decorator for any sub function called in an OpenUpgrade script
(pre, post or end migration script).

Decorate functions that may take time. If a function is decorated, we
provide an iterable argument that will be looped on and call the original
function, while a progress bar will be displayed.

Function: 28% (152 of 529) I#####              I Elapsed Time: 0:00:03 ETA: 0:01:32

:param index: Index of the argument to be used as iterable. Default to the
  second argument. It will pass each of the elements of the iterable in the
  same place.
:param title: Optional title for prefixing the progress bar. If not
  specified, the function name will be used.

Typical use::

@openupgrade.progress()
def migrate_some_stuff(env, record)
    # some custom code
    ...

@openupgrade.migrate()
def migrate(env, version):
    records = ...  # get an iterable
    migrate_some_stuff(env, records)

Co-Authored-By: Yann Papouin <[email protected]>
@pedrobaeza
Copy link
Member Author

Found the problem in Travis, that was a flake8 one.

@StefanRijnhart
Copy link
Member

This is nice, but would you consider making it optional by checking if a variable like OPENUPGRADE_DISABLE_PROGRESS is defined in the environment? I find myself watching logs in stdout so that I can observe any crashes in the same terminal. Of course, I capture the terminal output to file, and, like Sylvain, I would not like to have this log cluttered.

@pedrobaeza
Copy link
Member Author

Which method do you use for outputting the log to a file for looking for a way of having both?

@StefanRijnhart
Copy link
Member

I use tee, or script

@pedrobaeza
Copy link
Member Author

OK, I will check if there's any option.

@StefanRijnhart
Copy link
Member

I checked progressbar2, and there doesn't seem to be any. But you can choose to check in this code and if it is set, just execute the method instead of applying the progress bar.

@pedrobaeza
Copy link
Member Author

pedrobaeza commented Apr 20, 2021

Yeah, but then we won't benefit from the feature (I also do it this way: ... 2>&1 | tee ...).

@StefanRijnhart
Copy link
Member

That was my request, to make the feature optional ;-)

Copy link
Member

@yajo yajo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the dependency where it was missing.

@OCA-git-bot
Copy link
Contributor

This PR has the approved label and has been created more than 5 days ago. It should therefore be ready to merge by a maintainer (or a PSC member if the concerned addon has no declared maintainer). 🤖

@leemannd
Copy link
Contributor

Nice feature! @pedrobaeza Are you still willing to push forward and merge it?

@StefanRijnhart
Copy link
Member

Still hoping for an addition that allows me to disable this when I set OPENUPGRADE_DISABLE_PROGRESS=1 in the environment.

Copy link
Contributor

@legalsylvain legalsylvain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need rebase and fix conflict and add options.

@OCA-git-bot
Copy link
Contributor

This PR has the approved label and has been created more than 5 days ago. It should therefore be ready to merge by a maintainer (or a PSC member if the concerned addon has no declared maintainer). 🤖

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants