Skip to content
This repository has been archived by the owner on Apr 22, 2020. It is now read-only.

0.4.0 - a batch of little tweaks and fixes #175

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ install:
- pip install gevent PyYaml
- pip install sphinx==1.7.9 ghp-import

before_script:
# we need this for test_ziplog, which parses ambiguous timestamps
- export TZ=Asia/Jerusalem

script:
- python -m pytest -vv tests
- python -m pytest -vv --doctest-modules easypy
Expand Down
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.4.0]

### Changed
- Logging Initialization
- a run-once `initialize` method takes care of it all
- removed `set_width`, `set_coloring`, `set_graphics`
- the env-var `TERM_COLOR_SUPPORT` is no longer checked
- the env-var `EASYPY_LOG_INDENTATION` is no longer checked
- DataSize - return 'byte' and 'MiB' instead of '1 byte' and '1 MiB'

### Added
- ziplog
- expose as a cli tool - `ziplog`
- added commonly found timestamp patterns
- colors
- expose as a cli tool - `colorize
koreno marked this conversation as resolved.
Show resolved Hide resolved
- random: added `perchance(probabilty)`
- Examples
- a skeleton for initializing logging with easypy

### Deprecated
- use '|' instead of '&' for piping plumbum commands into loggers


## [0.3.0] - 2019-06-10

### Added
Expand Down
79 changes: 54 additions & 25 deletions easypy/collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,20 @@
from .exceptions import TException

import sys

from collections import OrderedDict as PythonOrderedDict
SUPPORT_GET_SIBLING = True

if sys.version_info[:2] >= (3, 5):
# In order to support 'get_prev' and 'get_next', we need access to OrderedDict's internal .__map,
# which we don't have in the C-implementation of the class in Python3.5
# This hack allows us to get to the pythonic implemenation of OrderedDict
from test.support import import_fresh_module
PythonOrderedDict = import_fresh_module('collections', blocked=['_collections']).OrderedDict
else:
from collections import OrderedDict as PythonOrderedDict
try:
from test.support import import_fresh_module
PythonOrderedDict = import_fresh_module('collections', blocked=['_collections']).OrderedDict
except ImportError:
# this happens on 3.6.7-8 due to a bug (https://bugzilla.redhat.com/show_bug.cgi?id=1651245)
SUPPORT_GET_SIBLING = False


def _format_predicate(pred):
Expand Down Expand Up @@ -553,27 +559,50 @@ def index(self, obj):
if lookup_uid == uid:
return i

def get_next(self, obj):
uid = self._get_object_uid(obj)
if uid not in self._objects:
raise ObjectNotFound(self, (), dict(key=uid),)
next_uid = self._objects._OrderedDict__map[uid].next
try:
key = next_uid.key
except AttributeError:
key = next_uid.next.key
return self._objects[key]

def get_prev(self, obj):
uid = self._get_object_uid(obj)
if uid not in self._objects:
raise ObjectNotFound(self, (), dict(key=uid),)
prev_uid = self._objects._OrderedDict__map[uid].prev
try:
key = prev_uid.key
except AttributeError:
key = prev_uid.prev.key
return self._objects[key]
if SUPPORT_GET_SIBLING:
koreno marked this conversation as resolved.
Show resolved Hide resolved
def get_next(self, obj):
uid = self._get_object_uid(obj)
if uid not in self._objects:
raise ObjectNotFound(self, (), dict(key=uid),)
next_uid = self._objects._OrderedDict__map[uid].next
try:
key = next_uid.key
except AttributeError:
key = next_uid.next.key
return self._objects[key]

def get_prev(self, obj):
uid = self._get_object_uid(obj)
if uid not in self._objects:
raise ObjectNotFound(self, (), dict(key=uid),)
prev_uid = self._objects._OrderedDict__map[uid].prev
try:
key = prev_uid.key
except AttributeError:
key = prev_uid.prev.key
return self._objects[key]
else:
def get_next(self, obj):
from warning import warn
warn("Couldn't import a pure-python OrderedDict so this will be a O(n) operation")

from itertools import cycle
l1, l2 = cycle(self._objects.values()), cycle(self._objects.values())
next(l2)
for a, b in zip(l1, l2):
if obj == a:
return b

def get_prev(self, obj):
from warning import warn
warn("Couldn't import a pure-python OrderedDict so this will be a O(n) operation")

from itertools import cycle
l1, l2 = cycle(self._objects.values()), cycle(self._objects.values())
next(l2)
for a, b in zip(l1, l2):
if obj == b:
return a

def keys(self):
return self._objects.keys()
Expand Down
21 changes: 15 additions & 6 deletions easypy/colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,19 @@
r"[A-Z_]+(?:\([^\)]+\))?"
r"(?:"
r"(?:\<\<).*?(?:\>\>)|"
r"(?:\@\{).*?(?:\}\@)"
r"(?:\@\{).*?(?:\}\@)|"
r"(?:\@\[).*?(?:\]\@)"
"))")

# this regex is used to parse the color markup into a foreground color, optional background, and the text itself.
# the text can be enclosed either by '<<..>>' or '@{...}@'
# the text can be enclosed either by '<<..>>' or '@[...]@'
RE_PARSE_COLOR_MARKUP = re.compile(
r"(?ms)"
r"([A-Z_]+(?:\([^\)]+\))?)" # group 0: the coloring
r"(?:"
r"\<\<(.*?)\>\>|" # group 1: first trap for text <<...>>
r"\@\{(.*?)\}\@" # group 2: second trap for text @{...}@
r"\@\{(.*?)\}\@|" # group 2: second trap for text @{...}@
r"\@\[(.*?)\]\@" # group 3: second trap for text @[...]@
")")


Expand Down Expand Up @@ -453,10 +455,17 @@ def register_colorizers(**styles):
globals().update((name.upper(), Colorizer(color=name, name=name)) for name in COLORS)


if __name__ == '__main__':
def main():
"""
Colorize lines from stdin
"""
import fileinput
for line in fileinput.input(openhook=functools.partial(open, errors='replace')):
print(colorize(line), end="", flush=True)
try:
for line in fileinput.input(openhook=functools.partial(open, errors='replace')):
print(colorize(line), end="", flush=True)
except BrokenPipeError:
pass


if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions easypy/concurrency.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
import os
from collections import namedtuple
from datetime import datetime
from unittest.mock import MagicMock

import easypy._multithreading_init # noqa; make it initialize the threads tree
from easypy.exceptions import PException
Expand Down
28 changes: 15 additions & 13 deletions easypy/humanize.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,16 @@ def time_ago(t, now=None):

INPUT_TIME_PATTERNS = [
((re.compile(p[0]),) + tuple(p[1:])) for p in (
("(\d{1,2}/\d{1,2}-\d{1,2}:\d{1,2}:\d{1,2})", "%d/%m-%H:%M:%S", 1, 0), # 21/05-17:09:59
("(\d{4}-\d{1,2}-\d{1,2}(?P<sep>[T-])\d{1,2}:\d{1,2}:\d{1,2})(?:\d*\.\d*)?(?P<utc>Z)?", "%Y-%m-%d{sep}%H:%M:%S", 0, time.timezone),
("(\d{4}-\d{1,2}-\d{1,2}-\d{1,2}:\d{1,2})", "%Y-%m-%d-%H:%M", 0, 0),
("(\d{1,2}-\d{1,2}-\d{1,2}:\d{1,2})", "%m-%d-%H:%M", 1, 0),
("(\w+\s+\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2})", "%b %d %H:%M:%S", 1, 0),
("(\d{1,2}-\d{1,2}:\d{1,2}:\d{1,2})", "%d-%H:%M:%S", 2, 0),
("(\d{1,2}-\d{1,2}:\d{1,2})", "%d-%H:%M", 2, 0),
("(\d{1,2}:\d{1,2}:\d{1,2})", "%H:%M:%S", 3, 0),
("(\d{1,2}:\d{1,2})", "%H:%M", 3, 0),
(r"(\d{1,2}/\d{1,2}-\d{1,2}:\d{1,2}:\d{1,2})", "%d/%m-%H:%M:%S", 1, 0), # 21/05-17:09:59
(r"(\d{4}-\d{1,2}-\d{1,2}(?P<sep>[T-])\d{1,2}:\d{1,2}:\d{1,2})(?:\d*\.\d*)?(?P<utc>Z)?",
"%Y-%m-%d{sep}%H:%M:%S", 0, time.timezone),
(r"(\d{4}-\d{1,2}-\d{1,2}-\d{1,2}:\d{1,2})", "%Y-%m-%d-%H:%M", 0, 0),
(r"(\d{1,2}-\d{1,2}-\d{1,2}:\d{1,2})", "%m-%d-%H:%M", 1, 0),
(r"(\w+\s+\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2})", "%b %d %H:%M:%S", 1, 0),
(r"(\d{1,2}-\d{1,2}:\d{1,2}:\d{1,2})", "%d-%H:%M:%S", 2, 0),
(r"(\d{1,2}-\d{1,2}:\d{1,2})", "%d-%H:%M", 2, 0),
(r"(\d{1,2}:\d{1,2}:\d{1,2})", "%H:%M:%S", 3, 0),
(r"(\d{1,2}:\d{1,2})", "%H:%M", 3, 0),
)]


Expand Down Expand Up @@ -214,10 +215,11 @@ def indent(self, fmt, *args, **kwargs):

def render(self, width=None, textual=None, prune=False, file=None, overflow='ignore', edges=True):
if width is None:
from .logging import TERM_WIDTH as width
from .logging import G
width, _ = os.get_terminal_size() if G.IS_A_TTY else [120, 0]
if textual is None:
from .logging import GRAPHICAL
textual = not GRAPHICAL
from .logging import G
textual = not G.GRAPHICAL

buff = file if file else StringIO()
G = self.TEXTUAL_BOX if textual else self.NICE_BOX
Expand Down Expand Up @@ -462,7 +464,7 @@ class TrimmingTemplate(str):

"""

RE_NEW_STYLE_FORMATTERS = re.compile("{(\w+):([^}]*)}")
RE_NEW_STYLE_FORMATTERS = re.compile(r"{(\w+):([^}]*)}")

def __init__(self, s):
assert s == to_new_style_formatter(s), "Can't use old-style string formatting syntax"
Expand Down
Loading