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

Refactor archive progress bar #4504

Merged
merged 16 commits into from
Oct 25, 2020

Conversation

chrisjsewell
Copy link
Member

As proposed in #4053 (comment), this PR refactors the progress bar implementation in for the archive export to remove the singleton approach and make all calls to the progress bar a context manager.
I also improved some progress bars to give better feedback (in particular the entity field collection).

I've tested this implementation (see below) and find no issues.

As an added bonus, I have also added a verbose option to the CLI (-v), such that this will set the progress bars to be left on screen (i.e. leave=True)

@chrisjsewell
Copy link
Member Author

Oct-23-2020 07-41-45

@codecov
Copy link

codecov bot commented Oct 23, 2020

Codecov Report

Merging #4504 into develop will decrease coverage by 0.01%.
The diff coverage is 94.84%.

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #4504      +/-   ##
===========================================
- Coverage    79.36%   79.36%   -0.00%     
===========================================
  Files          475      476       +1     
  Lines        34907    34950      +43     
===========================================
+ Hits         27701    27733      +32     
- Misses        7206     7217      +11     
Flag Coverage Δ
#django 73.22% <93.90%> (+0.01%) ⬆️
#sqlalchemy 72.45% <93.90%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
aiida/tools/importexport/common/config.py 100.00% <ø> (ø)
aiida/common/progress_reporter.py 71.88% <71.88%> (ø)
aiida/tools/importexport/dbexport/__init__.py 97.80% <98.73%> (-0.26%) ⬇️
aiida/cmdline/commands/cmd_export.py 90.91% <100.00%> (+0.47%) ⬆️
aiida/common/log.py 95.59% <100.00%> (+0.07%) ⬆️
aiida/tools/importexport/common/progress_bar.py 83.34% <100.00%> (ø)
.../importexport/dbimport/backends/django/__init__.py 91.55% <100.00%> (+0.03%) ⬆️
...ls/importexport/dbimport/backends/sqla/__init__.py 92.52% <100.00%> (+0.03%) ⬆️
aiida/transports/plugins/local.py 82.57% <0.00%> (-0.25%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1be12e1...694fb47. Read the comment docs.

@ltalirz
Copy link
Member

ltalirz commented Oct 23, 2020

Hi @chrisjsewell, before going through the code in detail, here is a question:

There is an open PR #3896 to homogenize verbosity handling across the whole verdi cli.
As is common with loggers, the approach taken there is to configure a VERDI_LOGGER singleton instance, which then has the advantage that one can simply VERDI_LOGGER.debug(...) or VERDI_LOGGER.warn(...).

I am wondering whether a similar approach would not work for the progress bar as well.
Is it really necessary to pass the instance around to every little sub function?
And by having the progress bar as a required argument we force anyone who would like to use these functions to set up a fake progress bar just to call it.

@chrisjsewell
Copy link
Member Author

chrisjsewell commented Oct 23, 2020

I am wondering whether a similar approach would not work for the progress bar as well.
Is it really necessary to pass the instance around to every little sub function?
And by having the progress bar as a required argument we force anyone who would like to use these functions to set up a fake progress bar just to call it.

Eurh, I think exactly the opposite; I think singleton's (and using mutable global variables in general) are awful and should only be used when absolutely necessary. I would point you, for example, towards https://stackoverflow.com/a/138012/5033292, https://stackoverflow.com/a/2663030/5033292 etc, for the multiple issues with the singleton design pattern

Basically I would advocate dependency injection over the singleton design pattern

@chrisjsewell
Copy link
Member Author

chrisjsewell commented Oct 23, 2020

if anything, the private functions in this class + the export function should be turned into a class, with a shared (local) state.
I would be happy to make this change

@ltalirz
Copy link
Member

ltalirz commented Oct 23, 2020

I think singleton's (and using mutable global variables in general) are awful and should only be used when absolutely necessary.

I think, as with many questions, there is a balance to be found.
Of course I'm aware of the drawbacks of global variables - at the same time, passing every variable around every time can hurt code readability.

Using loggers is a standard approach suggested in the official python tutorial https://docs.python.org/3/howto/logging.html#configuring-logging
Would you suggest that we start passing loggers around explicitly in aiida-core?
[in the context of functional programming, one eventually ends up in the question of whether to pass around "the screen" ;-) ]

In any case, I agree that the progress bar (which is around only temporarily and specific to this section of hte code) differs from loggers (which are assumed to be there all the time), so perhaps we will need to pass it around in the end.

if anything, the private functions in this class + the export function should be turned into a class, with a shared (local) state.
I would be happy to make this change

Yes, a more localized way of sharing state would make sense.
Whatever the solution, it would be helpful to have an obvious way of using functions that can interact with the progress bar also in scenarios where one doesn't have a progress bar at hand (unless they are really so specific that we cannot imagine anyone ever using them in a different context).

@chrisjsewell
Copy link
Member Author

Would you suggest that we start passing loggers around explicitly in aiida-core?

Well loggers are one of them rare use cases. Although still it can be a real pain to ensure the behaviour of logging, for example sphinx sets up a global logger which makes debugging failed tests a real annoyance (docutils/sphinx also have some other singletons which are horrible to override, and one of the reasons for my hatred of them lol).

@chrisjsewell
Copy link
Member Author

it would be helpful to have an obvious way of using functions that can interact with the progress bar also in scenarios where one doesn't have a progress bar at hand

Just to note, none of the public functions require that a progress bar be specifically passed

Copy link
Member

@ltalirz ltalirz left a comment

Choose a reason for hiding this comment

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

thanks @chrisjsewell

only minor comments

aiida/cmdline/commands/cmd_export.py Outdated Show resolved Hide resolved
aiida/tools/importexport/dbexport/__init__.py Outdated Show resolved Hide resolved
aiida/tools/importexport/dbexport/__init__.py Outdated Show resolved Hide resolved
aiida/tools/importexport/dbexport/__init__.py Outdated Show resolved Hide resolved
aiida/tools/importexport/dbexport/__init__.py Outdated Show resolved Hide resolved
aiida/tools/importexport/dbexport/__init__.py Show resolved Hide resolved
aiida/tools/importexport/dbexport/__init__.py Show resolved Hide resolved
aiida/tools/importexport/dbexport/__init__.py Outdated Show resolved Hide resolved
)
res = set(builder.all(flat=True))
given_log_entry_ids.update(res)
total = 1 + ((1 if include_logs else 0) + (1 if include_logs else 0) if node_ids_to_be_exported else 0)
Copy link
Member

Choose a reason for hiding this comment

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

that should probably be

Suggested change
total = 1 + ((1 if include_logs else 0) + (1 if include_logs else 0) if node_ids_to_be_exported else 0)
total = 1 + ((1 if include_logs else 0) + (1 if include_comments else 0) if node_ids_to_be_exported else 0)

Copy link
Member

Choose a reason for hiding this comment

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

by the way, if there are no node ids to be exported, what is the 1 for?

Copy link
Member Author

Choose a reason for hiding this comment

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

oops, yeh I was having an issue without the 1, but I will re-check

@chrisjsewell
Copy link
Member Author

chrisjsewell commented Oct 24, 2020

@ltalirz inspired by the discussion in #3896, I have re-re-factored in a54c319!

The progress bar is once again a singleton, but now it is fully generalised to just the concept of a progress reporter. As such it is not specific to importexport and so I have added its code as aiida/common/progress_reporter.py.

Control of setting the progress reporter is now moved to the CLI (perhaps it could somehow be integrated into #3896?), the exporter functions just retrieve it, with the default being a "null" reporter.

For now aiida/tools/importexport/common/progress_bar.py remains, but in the import refactor, this should be removed

@chrisjsewell
Copy link
Member Author

Note there is some code to set/reset the logging level in the export function, which could maybe be removed in #3896

@chrisjsewell
Copy link
Member Author

chrisjsewell commented Oct 24, 2020

In 19da2ad I have also moved control of the logger level (and formatting in 81f2118) to the CLI, so it should now be very easy to add in the #3896 code later

@chrisjsewell
Copy link
Member Author

chrisjsewell commented Oct 24, 2020

$ verdi export create --input-calc-forward -N 1 -G 1 -f -v DEBUG -- tmp/mount_folder/out2.aiida 
|
EXPORT
--------------  ---------------------------
Archive         tmp/mount_folder/out2.aiida
Format          Zip (compressed)
Export version  0.9

Inclusion rules
-----------------  ----
Include Comments   True
Include Logs       True

Traversal rules
---------------------------------  -----
Follow links input calc forwards   True
Follow links input calc backwards  True
Follow links create forwards       True
Follow links create backwards      True
Follow links return forwards       True
Follow links return backwards      False
Follow links input work forwards   False
Follow links input work backwards  True
Follow links call calc forwards    True
Follow links call calc backwards   True
Follow links call work forwards    True
Follow links call work backwards   True

STARTING EXPORT...
Retrieving Nodes from Groups ...         100.0%|█████████████████████████████████████████████████████| 2/2
Traversing provenance via links ...      100.0%|█████████████████████████████████████████████████████| 1/1
Initializing export of all entities      100.0%|█████████████████████████████████████████████████████| 3/3
Preparing entities - Groups              100.0%|█████████████████████████████████████████████████████| 2/2
GATHERING DATABASE ENTRIES...
Exporting Group fields                   100.0%|█████████████████████████████████████████████| 40066/40066
Exporting a total of 40067 database entries, of which 40065 are Nodes.
GATHERING GROUP ELEMENTS...
Exporting Groups ...                     100.0%|█████████████████████████████████████████████| 40065/40065
GATHERING NODE ATTRIBUTES AND EXTRAS...
Exporting Attributes and Extras          100.0%|█████████████████████████████████████████████| 40065/40065
Data extracted in     34 s.
ADDING DATA TO EXPORT ARCHIVE...
ADDING REPOSITORY FILES TO EXPORT ARCHIVE...
Exporting repository - UUID=f191b6cb     100.0%|█████████████████████████████████████████████| 40065/40065
Data written in 2.4e+02 s.
Success: wrote the export archive file to tmp/mount_folder/out2.aiida

Oct-24-2020 04-00-43

Copy link
Member

@ltalirz ltalirz left a comment

Choose a reason for hiding this comment

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

Hi @chrisjsewell , thanks for putting more thought into this an coming up with this solution.

I have some questions concerning the interface before finishing the review; will get back to the laptop later tonight

aiida/common/log.py Outdated Show resolved Hide resolved
aiida/common/progress_reporter.py Show resolved Hide resolved
aiida/common/log.py Outdated Show resolved Hide resolved
Copy link
Member

@ltalirz ltalirz left a comment

Choose a reason for hiding this comment

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

Thanks @chrisjsewell ; mostly requests for documentation + one simplification


Example Usage::

with progress_reporter(total=10, desc="A process:") as progress:
Copy link
Member

Choose a reason for hiding this comment

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

Is this supposed to be progress_reporter_base?

aiida/common/progress_reporter.py Outdated Show resolved Hide resolved
aiida/tools/importexport/dbexport/__init__.py Outdated Show resolved Hide resolved
aiida/common/progress_reporter.py Outdated Show resolved Hide resolved
aiida/common/progress_reporter.py Outdated Show resolved Hide resolved
aiida/common/progress_reporter.py Outdated Show resolved Hide resolved
@chrisjsewell
Copy link
Member Author

chrisjsewell commented Oct 25, 2020

@ltalirz in 1ab807a I have re-written the progress reporter contextmanager as a single class.
It works exactly the same as the decorated function, but I hope is a bit more easy to understand (it also works properly with auto-completion).

I have also added the set_progress_bar_tqdm convenience function

ltalirz
ltalirz previously approved these changes Oct 25, 2020
Copy link
Member

@ltalirz ltalirz left a comment

Choose a reason for hiding this comment

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

Thanks @chrisjsewell , at least to me this version of the progress reporter is much more obvious to interact with.

Let's merge this :-)
Edit: After the tests pass ;-)

@chrisjsewell chrisjsewell changed the title Refactor export progress bar Refactor archive progress bar Oct 25, 2020
@chrisjsewell chrisjsewell merged commit 33c9f41 into aiidateam:develop Oct 25, 2020
@chrisjsewell chrisjsewell deleted the export/progress-bar branch October 25, 2020 11:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants