Skip to content

Commit

Permalink
Merge branch 'master' into logging
Browse files Browse the repository at this point in the history
  • Loading branch information
ultrabug authored Oct 2, 2022
2 parents 3e3ff6d + afeab68 commit 0049671
Show file tree
Hide file tree
Showing 43 changed files with 1,514 additions and 859 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
strategy:
max-parallel: 5
matrix:
python-version: [3.6, 3.7, 3.8, 3.9]
python-version: [3.6, 3.7, 3.8, 3.9, "3.10"]

steps:
- uses: actions/checkout@v1
Expand Down
56 changes: 56 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,59 @@
version 3.46 (2022-08-25)
* INFORMATION: we plan to replace the bluetooth module with a more flexible one in coming releases (see #2114)
* core: use git package for logging git commit information if available (#2138), by Austin Lund
* docs: add Alpine Linux installation instructions (#2129), by raspbeguy
* new audiosink module: switch between pulseaudio sinks from your bar (#2137), by Jens Brandt
* sysdata module: ignore ZFS ARC cache in memory usage (#2131), by anarcat
* volume_status module: add start_delay to address issue #2136 (#2139)

version 3.45 (2022-06-16)
* official python3.6 support removed
* mail module: fix pathlib adoption (#2125), by Hunter Blanks
* twitch module: migrate to new Twitch API, fix #2123 (#2124), by Julian Picht
* volume_status module: use mapped volume with amixer backend. (#2128), by Jose Riha

version 3.44 (2022-04-22)
* IMPORTANT: python3.6 support will be removed on next release
* core: handle SIGTSTP/SIGCONT better for after sleep freezes
* formatter: stop escaping html characters on pango markup (#2122), by lasers
* module_test: add clear_timeout_due (#2119), by lasers
* udev monitor: implement udev events throttling with warnings
* google_calendar module: escape html on event summary for pango
* mpris module: add player_shortname placeholder for displaying player busname (#2120), by Valdur Kana
* xrandr module: fix missing " at the end of documentation line. (#2121), by Valdur Kana

version 3.43 (2022-04-01)
* core: rework SIGSTP inhibition to support both #2068 and #2107
* formatter: pango markup should escape all html character
* py3.safe_format: add information about invalid format
* py3.safe_format: implement a generic max_width parameter
* requirements: fix click dependency with black
* udev monitor: don't trigger actions when i3bar refresh is stopped
* weather_owm module: dont use onecall for better current day forecast (#2112)

version 3.42 (2022-03-14)
* core: fix root cause of negative update_due (#2109), by Valdur Kana
* external_script module: add option 'convert_numbers' to preserve number formatting (#2101), by Nicolas Kuttler
* khal_calendar module: add option to limit the number of returned calendar entries (#2103), by Andreas Grapentin
* mpris module: total rework with bug fixes (#2077), by Valdur Kana
* weather_own module: fix onecall when city is specified thx to @mlmatlock

version 3.41 (2022-02-27)
* core: drop and log negative new_update_due which causes high cpu load and endless loop (closses #2085) (#2092), by Valdur Kana
* core: switch from time.perf_counter to time.monotonic (#2099)
* core: timeout due to return something >= 0 instead of None
* new option py3status.stop_signal to disable i3bar stop/resume (#2095)
* arch_updates module: paru exits with a non-zero status if there are no updates (#2098), by Stefan G
* battery_level module: fallback to power_now if no current_now
* events: fix pydevd.debugger first click caused "JSONDecodeError: Expecting value: line 1 column 2 (char 1)". (closes #2090) (#2091), by Valdur Kana
* net_iplist module: support parsing iface name with "@" symbol (#2083), by Yikai Zhao
* pomodoro module: pyglet and pygame are no longer needed after the merge of #1770 (#2094), by Stefan G
* scratchpad module: workaround for i3ipc 2.2.1 not finding leaves() in sway (#2088), by Valdur Kana
* sysdata module: add tctl sensor (#2082), by lasers
* velib_metropole module: drop obsolete module
* weather_owm module: fix forecast by using new onecall api endpoint (#2097)
* window module: i3ipc backend uses incoming event data and relies less on get_tree.focused() data. (#2087), by Valdur Kana

version 3.40 (2021-11-08)
* README: update readthedocs links to new documentation thx to @oceyral
* i3 contrib page: update readthedocs links to new documentation
Expand Down
5 changes: 4 additions & 1 deletion docs/dev-guide/the-py3-helper.md
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ be added automatically following the convention:

returns: HttpResponse

### safe_format(format_string, param_dict=None, force_composite=False, attr_getter=None)
### safe_format(format_string, param_dict=None, force_composite=False, attr_getter=None, max_width=None)

Parser for advanced formatting.

Expand Down Expand Up @@ -436,6 +436,9 @@ returned.
attr_getter is a function that will when called with an attribute name
as a parameter will return a value.

max_width lets you to control the total max width of 'full_text' the
module is allowed to output on the bar.

### stop_sound()

Stops any currently playing sounds for this module.
Expand Down
8 changes: 8 additions & 0 deletions docs/user-guide/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ Check available USE flags if you need them!
$ emerge -a py3status
```

## Alpine Linux

Currently available on Edge version of Alpine. Make sure you enabled the community repository, then:

```bash
$ apk add py3status
```

## PyPi

```bash
Expand Down
126 changes: 94 additions & 32 deletions py3status/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def run(self):
self.notify_user(err)
else:
# check again in 5 seconds
self.timeout_queue_add(self, int(time.perf_counter()) + 5)
self.timeout_queue_add(self, int(time.monotonic()) + 5)


class ModuleRunner(Task):
Expand Down Expand Up @@ -258,8 +258,10 @@ def __init__(self, options):
"""
self.config = vars(options)
self.i3bar_running = True
self.i3bar_inhibit_stp = time.time()
self.last_refresh_ts = time.perf_counter()
self.last_loop_ts = time.monotonic()
self.last_refresh_ts = time.monotonic()
self.last_sigcont_ts = None
self.last_sigtstp_ts = None
self.lock = Event()
self.modules = {}
self.notified_messages = set()
Expand All @@ -284,6 +286,7 @@ def __init__(self, options):
self.timeout_missed = {}
self.timeout_queue = {}
self.timeout_queue_lookup = {}
self.timeout_queue_lookup_previous = {}
self.timeout_running = set()
self.timeout_update_due = deque()

Expand All @@ -300,6 +303,25 @@ def timeout_queue_add(self, item, cache_time=0):
if self.timeout_due is None or cache_time < self.timeout_due:
self.update_request.set()

def clear_timeout_due(self, module):
old = self.timeout_queue_lookup_previous.get(module, None)
if old:
if old == self.timeout_due:
self._set_new_timeout_due()
elif old in self.timeout_keys:
self.timeout_keys.remove(old)
self._set_new_timeout_due()

def _set_new_timeout_due(self):
# sort keys so earliest is first
self.timeout_keys.sort()

# when is next timeout due?
try:
self.timeout_due = self.timeout_keys[0]
except IndexError:
self.timeout_due = None

def timeout_process_add_queue(self, module, cache_time):
"""
Add a module to the timeout_queue if it is scheduled in the future or
Expand All @@ -316,7 +338,7 @@ def timeout_process_add_queue(self, module, cache_time):
return

# remove if already in the queue
key = self.timeout_queue_lookup.get(module)
key = self.timeout_queue_lookup.get(module, None)
if key:
queue_item = self.timeout_queue[key]
queue_item.remove(module)
Expand All @@ -327,24 +349,20 @@ def timeout_process_add_queue(self, module, cache_time):
if cache_time == 0:
# if cache_time is 0 we can just trigger the module update
self.timeout_update_due.append(module)
self.timeout_queue_lookup[module] = None
if module in self.timeout_queue_lookup.keys():
del self.timeout_queue_lookup[module]
else:
# add the module to the timeout queue
if cache_time not in self.timeout_keys:
self.timeout_queue[cache_time] = {module}
self.timeout_keys.append(cache_time)
# sort keys so earliest is first
self.timeout_keys.sort()

# when is next timeout due?
try:
self.timeout_due = self.timeout_keys[0]
except IndexError:
self.timeout_due = None
self._set_new_timeout_due()
else:
self.timeout_queue[cache_time].add(module)
# note that the module is in the timeout_queue
self.timeout_queue_lookup[module] = cache_time
self.timeout_queue_lookup_previous[module] = cache_time

def timeout_queue_process(self):
"""
Expand All @@ -353,7 +371,7 @@ def timeout_queue_process(self):
# process any items that need adding to the queue
while self.timeout_add_queue:
self.timeout_process_add_queue(*self.timeout_add_queue.popleft())
now = time.perf_counter()
now = time.monotonic()
due_timeouts = []
# find any due timeouts
for timeout in self.timeout_keys:
Expand Down Expand Up @@ -410,8 +428,9 @@ def timeout_queue_process(self):
Runner(module, self, module_name)

# we return how long till we next need to process the timeout_queue
# this value should not be negative to avoid cpu overwhelming loops
if self.timeout_due is not None:
return self.timeout_due - time.perf_counter()
return max(0, self.timeout_due - time.monotonic())

def gevent_monkey_patch_report(self):
"""
Expand Down Expand Up @@ -578,18 +597,36 @@ def setup(self):
"""
self._setup_logging()

# log py3status and python versions
self.log("=" * 8)
msg = "Starting py3status version {version} python {python_version}"
self.log(msg.format(**self.config))
def _log_gitversion(self):
# A git repo is detected looking for the .git directory


git_path = Path(__file__).resolve().parent.parent / ".git"
if not git_path.exists():
return

self.log("Running within git repo")

try:
# if running from git then log the branch and last commit
# we do this by looking in the .git directory
git_path = Path(__file__).resolve().parent.parent / ".git"
# branch
with (git_path / "HEAD").open() as f:
out = f.readline()
import git
except ImportError:
repo = None
else:
try:
repo = git.Repo(git_path.parent)
except Exception:
repo = None

if not repo:
try:
with (git_path / "HEAD").open() as f:
out = f.readline()
except OSError:
self.log(
"Unable to read git HEAD. "
"Use python git package for more repo information"
)
return
branch = "/".join(out.strip().split("/")[2:])
self.log(f"git branch: {branch}")
# last commit
Expand All @@ -599,8 +636,24 @@ def setup(self):
sha = out.split(" ")[1][:7]
msg = ":".join(out.strip().split("\t")[-1].split(":")[1:])
self.log(f"git commit: {sha}{msg}")
except: # noqa e722
pass
else:
commit = repo.head.commit
self.log(f"git branch: {repo.active_branch.name}")
self.log(f"git commit: {commit.hexsha[:7]} {commit.summary}")
self.log(f"git clean: {not repo.is_dirty()!s}")

def setup(self):
"""
Setup py3status and spawn i3status/events/modules threads.
"""

# log py3status and python versions
self.log("=" * 8)
msg = "Starting py3status version {version} python {python_version}"
self.log(msg.format(**self.config))

# if running from git then log the branch and last commit
self._log_gitversion()

self.log("window manager: {}".format(self.config["wm_name"]))

Expand Down Expand Up @@ -755,7 +808,7 @@ def notify_user(
limit_key = ""
if rate_limit:
try:
limit_key = time.perf_counter() // rate_limit
limit_key = time.monotonic() // rate_limit
except TypeError:
pass
# We use a hash to see if the message is being repeated. This is crude
Expand Down Expand Up @@ -829,8 +882,8 @@ def refresh_modules(self, module_string=None, exact=True):
refreshes.
"""
if not module_string:
if time.perf_counter() > (self.last_refresh_ts + 0.1):
self.last_refresh_ts = time.perf_counter()
if time.monotonic() > (self.last_refresh_ts + 0.1):
self.last_refresh_ts = time.monotonic()
else:
# rate limiting
return
Expand Down Expand Up @@ -995,18 +1048,25 @@ def process_module_output(self, module):
return ",".join(dumps(x) for x in outputs)

def i3bar_stop(self, signum, frame):
if time.time() - self.i3bar_inhibit_stp > 1:
if (
self.last_sigcont_ts
and self.last_sigtstp_ts
and self.last_sigcont_ts > self.last_sigtstp_ts
and time.monotonic() - self.last_loop_ts < 1
):
self.log(f"received stop_signal {Signals(signum).name}")
self.i3bar_running = False
# i3status should be stopped
self.i3status_thread.suspend_i3status()
self.sleep_modules()
else:
self.log(f"inhibited stop_signal {Signals(signum).name}")
self.log(f"inhibited stop_signal {Signals(signum).name}", level="warning")
self.last_sigtstp_ts = time.monotonic()

def i3bar_start(self, signum, frame):
self.log(f"received resume signal {Signals(signum).name}")
self.i3bar_inhibit_stp = time.time()
self.last_loop_ts = time.monotonic()
self.last_sigcont_ts = time.monotonic()
self.i3bar_running = True
self.wake_modules()

Expand Down Expand Up @@ -1079,6 +1139,8 @@ def run(self):
while not self.i3bar_running:
time.sleep(0.1)

self.last_loop_ts = time.monotonic()

# check if an update is needed
if self.update_queue:
while len(self.update_queue):
Expand Down
7 changes: 6 additions & 1 deletion py3status/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ def readline(self, timeout=500):
poll_result = self.poller.poll(timeout)
if poll_result:
line = self.io.readline().strip()
if self.io == sys.stdin and line == "[":
# when using pydev.debugger sys.stdin gets overwritten and placed
# into sys.stdin.original_stdin issue #2090
if (
self.io == getattr(sys.stdin, "original_stdin", sys.stdin)
and line == "["
):
# skip first event line wrt issue #19
line = self.io.readline().strip()
try:
Expand Down
9 changes: 1 addition & 8 deletions py3status/formatter.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import re

from math import ceil
from numbers import Number
from urllib.parse import parse_qsl

from py3status.composite import Composite
from py3status.constants import COLOR_NAMES, COLOR_NAMES_EXCLUDED

from urllib.parse import parse_qsl


def expand_color(color, default=None, passthrough=False, block=None):
"""
Expand Down Expand Up @@ -700,9 +698,4 @@ def render(self, get_params, module, _if=None):
out[0]["full_text"] = " " * min_length + out[0]["full_text"]
min_length = 0

# support pango special characters
for item in out:
if item.get("markup") == "pango" and "&" in item.get("full_text", ""):
item["full_text"] = item["full_text"].replace("&", "&amp;")

return valid, out
Loading

0 comments on commit 0049671

Please sign in to comment.