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

wakepy 0.8.0 release #297

Merged
merged 197 commits into from
May 25, 2024
Merged

wakepy 0.8.0 release #297

merged 197 commits into from
May 25, 2024

Conversation

fohrloop
Copy link
Owner

No description provided.

fohrloop and others added 30 commits August 6, 2023 16:43
Merge WIP 0.8.0dev to master

This is still WIP as will make master unusable (tests will fail and
the main functionality is broken). Checkout a version using a tag if
you want to get the source code of a working version.
update the old urls: np-8 -> fohrloop
* Add: Branches section
* Add: Testing section
* Add: How versions for docs are selected
fixes `__main__.py` for Python < 3.9

Error with Python < 3.9: `TypeError: 'type' object is not subscriptable`

Caused by using type annotation `dict` instead of `typing.Dict`.
* Reflect new tag names: v0.8.0 instead of 0.8.0
* master renamed to main
Add basic (alpha) version of ActivationResult

This considers *only* about the data storage and retrieval part of the
ActivationResult. In other words, there is no functionality which would
connect ActivationResult yet to the thread/queue for the data. This
will be added soon. 

Now the API could look something like this: 

```
with keep.running() as k:

    if not k.success:
        logger.warning('keepawake not successful')
        logger.warning(k.get_details())
        
    else: 
       logger.info('keepawake successful. Using "%s"', k.active_methods_string)
       # do something
```

Example log messages in case of failure:

WARNING:root:keepawake not successful
WARNING:root:[(FAIL @requirements, fail-requirements, "Missing requirement: Some SW v.1.2.3"), (SUCCESS, a-successful-method), (UNUSED, some-unused-method)]

Example log messages in case of success:

INFO:root:keepawake successful. Using "a-successful-method"
or
INFO:root:keepawake successful. Using "1st.successful.method, 2nd-successful-method & last-successful-method"

The ActivationResult contains now: 

Attributes
-----------
.success: bool
.real_success: bool
.failure: bool 
.active_methods: list[str]
.active_methods_string: str 

Methods
-------
get_details (higher-level interface)
get_detailed_results (lower-level interface)
… ModeActivator (WIP) (#70)

* reorder attrs and dosctring (MethodUsageResult)

* Split Mode into parts

* Mode class simplified. Now it was only following methods

  __init__ for creating Modes (typically not for end users)
  __call__ for setting some settings for the Mode. Not needed in very
           basic usage (for power users)
  __enter__ for context manager protocol
  __exit__ for context manager protocol

  and these attributes:

  .methods (list of Method classes to be used)
  .manager (the activation manager)

* The Mode has now only these responsibilities:
  1) Offer easy way of defining Modes as ordered collections of methods
  2) Provide the main wakepy API in the form on context managers

* Split dbus adapter resolution from Mode to calls.py. This should be
  responsibility of the future Caller class.

* Create modeactivator.py and move the ModeWorkerThread there. In the
  future, there should be a ModeActivator class here which does the
  heavy lifting and activates/deactivates Modes using Methods.

  TODO: Create the ModeActivator class

* Split ModeActivationManager out from the Mode. This is also WIP and
  not functional / tests.

  TODO: Finish and make tests for ModeActivationManager class.
Changes:
-------
1) keep.running is a function
keep.running and keep.presenting are now functions which return
instances of Modes. This is needed/helpful for supporting wakepy in the
multithreaded use case, as different threads calling keep.running() will
 get different object instances and will not share objects (other than
 "fixed code" such as classes)

2) Basic tests for Mode
Test that Mode fulfills the context manager protocol and that it uses
the "ModeActivationManager" as intended.

Todoes:
-------
Check if special cases need to be handled in Mode.__exit__ (+add code
and tests for those, if needed)
- Completed `Mode.__exit__()`: It now skips `ModeExit`  exception which
  can be used to exit the with statement block of a wakepy mode.
- Added tests for the `Mode.__exit__()`.
* Rename modemanager.py to activationmanager.py
* Cleanup code (isort, ruff, mypy)

No functional changes
Add "prioritize" parameter mode Mode.__init__, which is passed forward
to manager.activate(). This can be used to prioritize some methods over
others (needs still some thinking)

Add docstring for ModeActivationManager

Update ModeActivationManager.activate: This now was very basic
implementation for activating a mode. It basically forwards everything
to the another thread, where (in the future) a ModeActivator is running.
It also creates ActivationResult using new __init__() signature;
ActivationResul(manager). Idea is that ActivationResult does not have to
know anything about threads or queues; manager will handle that.

Add some implementation of ModeActivationManager.deactivate

The .activate and .decactivate will likely need some more updates later
on, and ModeActivationManager will surely need new methods to be
consumed by the ActivationResult. These will be addressed later.
This is a temporary solution. Just skip the currently non-working
tests so that it is easier to run the tests which do work. Much easier
to develop when you can run tests with one command.

TODO: Fix the tests
Ran isort + black + ruff. No functional changes.
These had been broken by some previous refactoring process.
Update the test instructions: Added notes for tox
* fix should_fake_success and add tests for it.

Previously, should_fake_success was always returning True if
WAKEPY_FAKE_SUCCESS was set to any value as for example bool('0') is
True.

* fix tox tests (missing jeepney)

The tox tests were missing a requirement (jeepney)

* fix tests for activationresult

The test logic was broken in one case, and WAKEPY_FAKE_SUCCESS was not
set correctly for the test. No functional changes.
* All Methods have default name of None.
* Methods (subclasses of Method) with a non-None names will be automatically registered.
* Function get_method_class to get the Method class based on name (str).
* Give names for Gnome methods: "org.gnome.SessionManager:Inhibit:Suspend" and "org.gnome.SessionManager:Inhibit:Idle". (initial; could be changed later)
This is a package of bunch of mypy related fixes.

Also removed the redundant _GnomeSessionManager.name = None (which is the default)
Add MethodCurationOpts class which has the following purpose

* Act as a data storage to method selection and prioritization parameters.
* Provide basic validation for those input parameters
* Convert the input parameters from strings to Method classes (constructor
  MethodCurationOpts.from_names)

Add function get_method_classes, which can be used to get a list, tuple or set of Method classes based on the Method names (strings)
Removed: set_keepawake, unset_keepawake, keepawake

The 0.8.0 release will have the same functionality as 0.7.0
but with the new API: keep.running and keep.presenting

The removed parts were deprecated 2023-06-11 in
wakepy 0.7.0
The StringConstant is really much like the enum.StrEnum or the
strenum.StrEnum. It makes the code a bit easier to read for others if
the same term (StrEnum) is used also here.
Add: get_methods_for_mode function
* A simple function for getting all the Method classes for a given Mode
name.

Add: get_selected_methods
* Function that gets Methods for a Mode and then optionally filters them
with "skip" or "use_only"

Add: ModeName constant
* Holds the names of different Modes wakepy supports

Add: Method.name: Modename attribute

Change: Improve docs of strenum.py and keep.running()

Rename get_method_class to get_method
* More unified naming with the rest of the functions which get method
classes

Rename get_method_classes to method_names_to_classes
* The new name is more descriptive to what the function actually does.
It converts a tuple, set or list of method names to method classes.

Rename: definitions.py to constants.py
old: get_selected_methods(methodname, skip, use_only)
- methodname: str

new: select_methods(methods, skip, use_only)
- methods: collection of Method

motivation:
Better separation of concerns and responsibilities.
Change Method.__init__ signature from 


def __init__(self, dbus_adapter: Optional[DBusAdapter] = None) -> None:
    self._dbus_adapter = dbus_adapter

to

def __init__(self, **kwargs: object) -> None:
    self.dbus_adapter = kwargs.pop("dbus_adapter", None)

this makes subclassing and explaining subclassing easier; instead of using

def __init__(self, dbus_adapter: Optional[DBusAdapter] = None) -> None:
    super().__init__(dbus_adapter=dbus_adapter)

which is confusing when a Method does not need dbus at all, user has to
define simply:

def __init__(self, **kwargs: object) -> None:
    super().__init__(**kwargs)

In addition, this is one step closer to #256 
(support for passing kwargs to Methods).
ActivationResult
----------------
Implement __eq__ and __repr__ (use dataclass). Now it's possible to
compare two ActivationResults with "==", and the ActivationResult when
printed has more infromation.

Before:
<wakepy.core.activation.ActivationResult at 0x70af4d994f70>

Now:
ActivationResult(modename='foo', active_method='a-successful-method',
success=True, real_success=True, failure=False, _method_results=[
(FAIL @PLATFORM_SUPPORT, fail-platform, "Platform XYZ not supported!"),
(FAIL @requirements, fail-requirements, "Missing requirement: Some SW
v.1.2.3"), (SUCCESS, a-successful-method), (UNUSED, some-unused-method)
])

In addition, refactor ActivationResult related tests.
…pyFakeSuccess into _testing module (#259)

Repo Maintenance
----------------
1) Split contents of wakepy.core.activation module into
wakepy.core.mode, wakepy.core.method and wakepy.core.activationresult

2) Put WakepyFakeSuccess into it's own module (wakepy.methods._testing).

3) Refactor some tests
* Split some combined tests into separate ones
* Use more fixtures. Remove some mocks.
Refactor: Prioritization
-------------------------
* Split prioritization related functions into it's own module.
* Rename: order_methods_by_priority (was: get_prioritized_methods)
* Mark rest of the priority related functions private (they're not
  used anywhere else)

+ tests refactoring
+ make Mode._activate signature simpler
* Checking WAKEPY_FAKE_SUCCESS env variable is only done in
  Mode._activate. If that is set to truthy value, insert the
  WakepyFakeSuccess method to the start of the methods list.
  This means that any Mode is quaranteed to be succesful with
  WAKEPY_FAKE_SUCCESS, but there's no guarantee for any particular
  Method (using Methods separately is not part of the public API 
  of wakepy, so this does not matter)
* WakepyFakeSuccess Method now always succeeds
* Always prioritize WAKEPY_FAKE_SUCCESS method to be the first, 
  if it is used.
* Add 'N', 'F' and the empty string to the list of falsy values. (previously just '0', 'False', and 'no')
* Update WAKEPY_FAKE_SUCCESS docs.
…ult (#263)

Update Mode activation
* Return list of MethodActivationResult instead a single
  ActivationResult. This way the function does not require method
  name as arg.
* Fix: Also the unused results are included in ActivationResult
 (and list of MethodActivationResult).
* Try to create the DbusAdapter instance for the Mode only once, even if
  the creation was unsuccesful the previous time.
  
Simplifications
* Simplify the Mode._activate
* Move activate_mode to Mode._activate_one_of_methods and simplify
* Simplify the Mode._activate_one_of_methods tests and remove Mocks.
Small refactoring, renames & tidying the tests
…s on py37-py39 (#265)

As wakepy is a type annotated library and supports python versions
from 3.7 to 3.12, and supported type annotations depend on the used
version of python, the mypy checks should be running also on versions
other than 3.10. 

Added mypy checks to CI pipelines, running on Python 3.7, 3.8, 3.9, 3.11
and 3.12

This revealed that the static type checks were failing on versions
3.7-3.9. Fixed the errors.
…E_SUCCESS + add test for missing methods in a Mode (#266)

Be more explicit in tests about which test is settings the 
WAKEPY_FAKE_SUCCESS.

Add test: Mode without any methods (empty list) should still activate
if WAKEPY_FAKE_SUCCESS is used.
Rename the "methods" parameter of Mode to "method_classes". The
attribute name is also "method_classes".
Add two new properties for Mode:
.active_method: str | None
  The name of the currently active method, if any.
.used_method: str | None
  The name of the currently or previously active method, if any.
  
Example:

>>> with keep.running() as mode:
        print('Active:', mode.active_method)
        print('Used:', mode.used_method)
    print('deactivated')
    print('Active:', mode.active_method)
    print('Active:', mode.used_method)

Active: org.gnome.SessionManager
Used: org.gnome.SessionManager
deactivated
Active: None
Active: org.gnome.SessionManager
The usage of MethodError does not seem systematic, and it is not clear
when to use it. Replaced MethodError in the few places it was used with
RuntimeError (MethodError's superclass).

Could add a MethodError or similar back if there is clear need for it.
But for now, keeping it simple.
was: get_error_text()

There are attributes called 'success' and 'failure' so it
makes more sense if the method name is get_failure_text().
Went through the public API parts of wakepy for the 0.8.0 release, and
updated the API Reference documentation. While fixing the docs, found
and fixed few other additional things.

Docs
----
* Add Method, ActivationResult, MethodActivationResult, ActivationError,
 MethodExit and PlatformName to API Reference.
* Add a lot of more cross-linking in the API Reference.
* Link colors: links to darker blue, inline code from fuchsia to
 brownish red.
* List items paragraphs: Remove extra space from above, and add some
 space below.

Method
------
* Remove .description attribute. Was never used anywhere.
* supported_platforms: Made any Method by default to support *any*
  platforms. This is therefore used as a whitelist. The person
  subclassing Method may then either use the automatic platform
  support check (with supported_platforms), or implement caniuse(), or
  just let the code crash on unsupported systems, which will be then
  handled and the method would be marked as failing one.
  
Naming: mode_name
------------------
* Use mode_name and method_name consistently everywhere
* Rename: Method.mode -> Method.mode_name. Makes it easier to
  differentiate if talking about Mode instance or a string.
* Rename the Mode.from_modename parameter to mode_name
* Rename: ActivationResult.modename -> .mode_name
* Add: MethodActivationResult.mode_name

Dbus Adapter
------------
* Make the wakepy.dbus_adapters.jeepney.JeepneyDBusAdapter importable
  from wakepy.JeepneyDBusAdapter using lazy-loading from PEP-562.
* Add tests for python 3.8, 3.9, 3.11 and 3.13
* Test with all versions of python on linux
* Test with only the oldest and newest version of python on macos and
  windows
* Always run mypy for /src in tests. Do this in the test job instead of
  using a separate job.
  

Test matrix
-----------
was: 3.7, 3.10, 3.12 (linux, windows, mac)

now: 3.7 - 3.13 (linux)
     3.7 & 3.13 (windows, mac
* Mention that Heartbeat methods are not yet fully implemented
* Small cosmetic touches
Add a new step in GitHub Actions: Show uncovered lines

Otherwise will not see the uncovered lines at all, as running the
actions will stop on first fail.
Fixes the error with Gnome SessionManager based keepawake (issue #276)

DBus Adapters
-------------
* All DBusAdapters now have to keep reference to the connection(s).
  This was required as if the connection instance was garbage collected,
  the inhibit cookie created by Gnome SessionManager was wiped. 
  
* The DBusAdapter class now has _get_connection(bus) method or getting
  a connection instance for an adapter.
  
* The DBusAdapter subclasses now must implement a
  _create_new_connection(bus) method.
  
Other
----

Temporarily ignore warnings in tests/integration/conftest.py while
running gc.collect(). This should be addressed later (likely as part of
#277)
Previously: When running tests with tox, and if there's a warning
issued, the tests fail. This option is not used in tasks.py (for
"invoke test").

Now: Warnings will fail tests also when running "invoke test" (or:
"inv test").
…77) (#282)

Previously: Using the org.gnome.SessionManager method repeatedly would
freeze system in 400-600 iterations.

Now: Get to ~2000 iterations before system freezes. 

Contents
--------
* Dbus connections are now closed. In addition, gc.collect() is called
  each time connection is closed (at least for now), as adding it helped
  to get more iteration cycles.
* Add warning to docs of org.gnome.SessionManager
* bonus: Some refactoring of dbus adapter tests + remove the temporary
  warning ignore introduced in #278.
The previous commit said:
Get to ~2000 iterations before system freezes

It should have said: 
Mouse just starts to feel laggy after 2000 iterations, but the system
will not freeze even with 10k iterations. After finishing, mouse is
permanently laggy until relogin.
Another PR for #68

Docs
-----
- Add nicer platform support table
- Add ActivationWarning to docs
- Update the docs front page
- Add Tutorial page (on-fail action, which method was used)

Other
-----
- Add: ActivationWarning. Use that instead of general UserWarning
  if on-fail action is warning.
@fohrloop
Copy link
Owner Author

This is a huge release, wakepy 0.7.2 -> 0.8.0. I'm also trying the automated release pipeline for the first time. Seems that it asks for a review on this one but since I'm the only one working on this let's see if I can do it or if I should disable the review requirement (which is a pity as some tools give some score to repos for that setting).

@fohrloop
Copy link
Owner Author

Okay it says that "This branch can't be rebased" so I guess the rebase strategy can't be used for this merge (perhaps because of what the history looks now)..

image

@fohrloop fohrloop merged commit bd948a5 into main May 25, 2024
14 checks passed
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.

2 participants