openSUSE Commits
Threads by month
- ----- 2025 -----
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
September 2022
- 1 participants
- 2392 discussions
Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-jsbeautifier for openSUSE:Factory checked in at 2022-09-30 17:57:57
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-jsbeautifier (Old)
and /work/SRC/openSUSE:Factory/.python-jsbeautifier.new.2275 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-jsbeautifier"
Fri Sep 30 17:57:57 2022 rev:9 rq:1007081 version:1.14.6
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-jsbeautifier/python-jsbeautifier.changes 2020-05-26 17:49:05.639850781 +0200
+++ /work/SRC/openSUSE:Factory/.python-jsbeautifier.new.2275/python-jsbeautifier.changes 2022-09-30 17:58:16.793310874 +0200
@@ -1,0 +2,73 @@
+Thu Sep 29 14:11:29 UTC 2022 - Yogalakshmi Arunachalam <yarunachalam(a)suse.com>
+
+- Update to v1.14.6
+ * Globs no longer work on Windows in 1.14.5 (#2093)
+- Update to v1.14.5
+ * Dependency updates and UI tweaks (#2088)
+ * Bump terser from 5.12.1 to 5.14.2 (#2084)
+ * new layout breaks everything on long lines (#2071)
+ * Dark mode (#2057)
+- Update to v1.14.4
+ * Extra space before !important added (#2056)
+ * css format removes space after quoted value (#2051)
+ * Add grid-template-areas to NON_SEMICOLON_NEWLINE_PROPERTY list (#2035)
+ * CSS formatter removes useful space (#2024)
+ * CHANGELOG.md file was wiped out in v1.14.2 (#2022)
+ * Fails to recognize Handlebars block with whitespace control, e.g. {{~#if true ~}} (#1988)
+ * Support new sass @use syntax (#1976)
+ * Do not remove whitespace after number (#1950)
+ * html formatter doesn't support handlebars partial blocks (#>) (#1869)
+ * in keyword in class method causes indentation problem (#1846)
+ * space_after_named_function not working inside an ES6 class (#1622)
+ * Restyle website (#1444)
+ * improper line concatenation between 'return' and a prefix expression (#1095)
+- Update to v1.14.3
+ * [LESS] Fixing issues with spacing when an object literal lives inside a mixin (#2017)
+ * Overindentation when using "class" as a key in an object (#1838)
+ * CSS Grid template formatting is broken when adding track size after line names (#1817)
+ * SCSS module system @use problem (#1798)
+ * JS "space_in_empty_paren" failing for class methods (#1151)
+ * LESS mixins gets formatted strangely (#722)
+- Update to v1.14.2
+ * Why put npm in dependencies? (#2005)
+ * [Bug] Logical assignments in JS are incorrectly beautified (#1991)
+- Update to v1.14.1
+ * feature request: cmd+enter hotkey for mac users (#1985)
+ * Wrong indentation when the last line in a case is a right brace (#1683)
+- Update to v1.14.0
+ * import.meta appears on newline (#1978)
+ * Added buttons to website (#1930)
+ * Logical assignment operators; Fix parsing of optional chaining (#1888)
+ * Numbers should be allowed to contain underscores (#1836)
+ * Use native mkdirSync instead of 'mkdirp' package (#1833)
+ * selector_separator_newline adds erroneous newline on @extend SCSS statements (#1799)
+- Update to v1.13.13
+ * IE11 compatibility failure v>1.13.5 (#1918)
+- Update to v1.13.11
+ * Support short PHP tags (#1840)
+- Update to v1.13.6
+ * Fix space-before-conditional: false to work on switch-case statement (#1881)
+ * Optional chaining obj?.[expr] (#1801)
+- Update to v1.13.1
+ * Option 'max_preserve_newlines' not working on beautify_css.js CSS Beautifier (#1863)
+ * React Fragment Short Syntax <></> issue (#1854)
+ * add viewport meta tag to index.html (#1843)
+ * Add basic smarty templating support (#1820)
+ * Tagged Template literals (#1244)
+- Update to v1.13.0
+ * (internal) Refactor python cssbeautifier to reuse jsbeautifier CLI methods (#1832)
+ * (internal) Switch from node-static to serve (#1831)
+ * Fixed pip install cssbeautifier (#1830)
+- Update to v1.12.0
+ * Python jsbeautifier fails for special chars (#1809)
+ * pip install cssbeautifier fails (#1808)
+ * Add expand brace-style option to css beautifier (#1796)
+ * Support nullish-coalescing (#1794)
+ * Upgrade ga.js to analytics.js (#1777)
+ * Newline rule not working with css-like files (#1776)
+ * no new line after self closing tag (#1718)
+ * HTML format, no break after ? (#1365)
+ * Does this extension still supports applying Allman style to CSS? (#1353)
+ * Add brace_style option for CSS (#1259)
+
+-------------------------------------------------------------------
Old:
----
jsbeautifier-1.11.0.tar.gz
New:
----
jsbeautifier-1.14.6.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-jsbeautifier.spec ++++++
--- /var/tmp/diff_new_pack.ErXYPg/_old 2022-09-30 17:58:17.229311805 +0200
+++ /var/tmp/diff_new_pack.ErXYPg/_new 2022-09-30 17:58:17.233311814 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-jsbeautifier
#
-# Copyright (c) 2020 SUSE LLC
+# Copyright (c) 2022 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-jsbeautifier
-Version: 1.11.0
+Version: 1.14.6
Release: 0
Summary: JavaScript unobfuscator and beautifier
License: MIT
@@ -33,7 +33,7 @@
Requires: python-setuptools
Requires: python-six >= 1.13.0
Requires(post): update-alternatives
-Requires(postun): update-alternatives
+Requires(postun):update-alternatives
BuildArch: noarch
# SECTION test requirements
BuildRequires: %{python_module EditorConfig >= 0.12.2}
++++++ jsbeautifier-1.11.0.tar.gz -> jsbeautifier-1.14.6.tar.gz ++++++
++++ 6282 lines of diff (skipped)
1
0
Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-jsonlines for openSUSE:Factory checked in at 2022-09-30 17:57:57
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-jsonlines (Old)
and /work/SRC/openSUSE:Factory/.python-jsonlines.new.2275 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-jsonlines"
Fri Sep 30 17:57:57 2022 rev:3 rq:1007080 version:3.1.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-jsonlines/python-jsonlines.changes 2021-11-08 17:25:21.996739896 +0100
+++ /work/SRC/openSUSE:Factory/.python-jsonlines.new.2275/python-jsonlines.changes 2022-09-30 17:58:16.101309395 +0200
@@ -1,0 +2,10 @@
+Thu Sep 29 14:16:46 UTC 2022 - Yogalakshmi Arunachalam <yarunachalam(a)suse.com>
+
+- Update to 3.0.0
+ * add type annotations; adopt mypy in strict mode (#58, #62)
+ * ignore UTF-8 BOM sequences in various scenarios (#69)
+ * support dumps() callables returning bytes again (#64)
+ * add basic support for rfc7464 text sequences (#61)
+ * drop support for numbers.Number in type= arguments (#63)
+
+-------------------------------------------------------------------
Old:
----
2.0.0.tar.gz
New:
----
3.1.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-jsonlines.spec ++++++
--- /var/tmp/diff_new_pack.WBnVUk/_old 2022-09-30 17:58:16.573310404 +0200
+++ /var/tmp/diff_new_pack.WBnVUk/_new 2022-09-30 17:58:16.577310412 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-jsonlines
#
-# Copyright (c) 2021 SUSE LLC
+# Copyright (c) 2022 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%global skip_python2 1
Name: python-jsonlines
-Version: 2.0.0
+Version: 3.1.0
Release: 0
Summary: Library with helpers for the jsonlines file format
License: BSD-3-Clause
++++++ 2.0.0.tar.gz -> 3.1.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonlines-2.0.0/.gitignore new/jsonlines-3.1.0/.gitignore
--- old/jsonlines-2.0.0/.gitignore 2021-01-04 17:03:18.000000000 +0100
+++ new/jsonlines-3.1.0/.gitignore 2022-07-01 18:35:18.000000000 +0200
@@ -4,8 +4,9 @@
# Testing
/.coverage
-/.tox/
+/coverage.xml
/htmlcov/
+/.tox/
# Packaging
/.cache/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonlines-2.0.0/LICENSE.rst new/jsonlines-3.1.0/LICENSE.rst
--- old/jsonlines-2.0.0/LICENSE.rst 2021-01-04 17:03:18.000000000 +0100
+++ new/jsonlines-3.1.0/LICENSE.rst 2022-07-01 18:35:18.000000000 +0200
@@ -1,6 +1,6 @@
*(This is the OSI approved 3-clause "New BSD License".)*
-Copyright �� 2016, Wouter Bolsterlee
+Copyright �� 2016, wouter bolsterlee
All rights reserved.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonlines-2.0.0/README.rst new/jsonlines-3.1.0/README.rst
--- old/jsonlines-2.0.0/README.rst 2021-01-04 17:03:18.000000000 +0100
+++ new/jsonlines-3.1.0/README.rst 2022-07-01 18:35:18.000000000 +0200
@@ -7,6 +7,9 @@
.. image:: https://pepy.tech/badge/jsonlines/month
:target: https://pepy.tech/project/jsonlines
+.. image:: https://anaconda.org/anaconda/anaconda/badges/installer/conda.svg
+ :target: https://anaconda.org/anaconda/jsonlines
+
=========
jsonlines
=========
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonlines-2.0.0/doc/index.rst new/jsonlines-3.1.0/doc/index.rst
--- old/jsonlines-2.0.0/doc/index.rst 2021-01-04 17:03:18.000000000 +0100
+++ new/jsonlines-3.1.0/doc/index.rst 2022-07-01 18:35:18.000000000 +0200
@@ -179,6 +179,34 @@
Version history
===============
+* 3.1.0, released at 2022-07-01
+
+ * Return number of chars/bytes written by :py:meth:`Writer.write()`
+ and :py:meth:`~Writer.write_all()`
+ (`#73 <https://github.com/wbolster/jsonlines/pull/73>`_)
+
+ * allow ``mode='x'`` in :py:func:`~jsonlines.open()`
+ to open a file for exclusive creation
+ (`#74 <https://github.com/wbolster/jsonlines/issues/74>`_)
+
+* 3.0.0, released at 2021-12-04
+
+ * add type annotations; adopt mypy in strict mode
+ (`#58 <https://github.com/wbolster/jsonlines/pull/58>`_,
+ `#62 <https://github.com/wbolster/jsonlines/pull/62>`_)
+
+ * ignore UTF-8 BOM sequences in various scenarios
+ (`#69 <https://github.com/wbolster/jsonlines/pull/69>`_)
+
+ * support ``dumps()`` callables returning bytes again
+ (`#64 <https://github.com/wbolster/jsonlines/issues/64>`_)
+
+ * add basic support for rfc7464 text sequences
+ (`#61 <https://github.com/wbolster/jsonlines/pull/61>`_)
+
+ * drop support for ``numbers.Number`` in ``type=`` arguments
+ (`#63 <https://github.com/wbolster/jsonlines/issues/63>`_)
+
* 2.0.0, released at 2021-01-04
* drop support for end-of-life Python versions; this package is now
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonlines-2.0.0/jsonlines/jsonlines.py new/jsonlines-3.1.0/jsonlines/jsonlines.py
--- old/jsonlines-2.0.0/jsonlines/jsonlines.py 2021-01-04 17:03:18.000000000 +0100
+++ new/jsonlines-3.1.0/jsonlines/jsonlines.py 2022-07-01 18:35:18.000000000 +0200
@@ -3,9 +3,36 @@
"""
import builtins
+import codecs
+import enum
+import io
import json
-import numbers
+import os
+import sys
+import types
+import typing
+from typing import (
+ Any,
+ Callable,
+ Dict,
+ Iterable,
+ Iterator,
+ List,
+ Optional,
+ Tuple,
+ Type,
+ TypeVar,
+ Union,
+ cast,
+ overload,
+)
+
+if sys.version_info >= (3, 8):
+ from typing import Literal
+else:
+ from typing_extensions import Literal # pragma: no cover
+import attr
VALID_TYPES = {
bool,
@@ -13,19 +40,59 @@
float,
int,
list,
- numbers.Number,
str,
}
+# Characters to skip at the beginning of a line. Note: at most one such
+# character is skipped per line.
+SKIPPABLE_SINGLE_INITIAL_CHARS = (
+ "\x1e", # RFC7464 text sequence
+ codecs.BOM_UTF8.decode(),
+)
+
+class DumpsResultConversion(enum.Enum):
+ LeaveAsIs = enum.auto()
+ EncodeToBytes = enum.auto()
+ DecodeToString = enum.auto()
+
+
+# https://docs.python.org/3/library/functions.html#open
+Openable = Union[str, bytes, int, os.PathLike]
+
+LoadsCallable = Callable[[Union[str, bytes]], Any]
+DumpsCallable = Callable[[Any], Union[str, bytes]]
+
+# Currently, JSON structures cannot be typed properly:
+# - https://github.com/python/typing/issues/182
+# - https://github.com/python/mypy/issues/731
+JSONCollection = Union[Dict[str, Any], List[Any]]
+JSONScalar = Union[bool, float, int, str]
+JSONValue = Union[JSONCollection, JSONScalar]
+TJSONValue = TypeVar("TJSONValue", bound=JSONValue)
+
+TRW = TypeVar("TRW", bound="ReaderWriterBase")
+
+default_loads = json.loads
+
+
+def default_dumps(obj: Any) -> str:
+ """
+ Fake dumps() function to use as a default marker.
+ """
+ raise NotImplementedError # pragma: no cover
+
+
+(a)attr.s(auto_exc=True, auto_attribs=True)
class Error(Exception):
"""
Base error class.
"""
- pass
+ message: str
+(a)attr.s(auto_exc=True, auto_attribs=True, init=False)
class InvalidLineError(Error, ValueError):
"""
Error raised when an invalid line is encountered.
@@ -42,24 +109,30 @@
"""
#: The invalid line
- line = None
+ line: Union[str, bytes]
#: The line number
- lineno = None
+ lineno: int
- def __init__(self, msg, line, lineno):
- msg = f"{msg} (line {lineno})"
+ def __init__(self, message: str, line: Union[str, bytes], lineno: int) -> None:
self.line = line.rstrip()
self.lineno = lineno
- super().__init__(msg)
+ super().__init__(f"{message} (line {lineno})")
+(a)attr.s(auto_attribs=True, repr=False)
class ReaderWriterBase:
"""
Base class with shared behaviour for both the reader and writer.
"""
- def close(self):
+ _fp: Union[typing.IO[str], typing.IO[bytes], None] = attr.ib(
+ default=None, init=False
+ )
+ _closed: bool = attr.ib(default=False, init=False)
+ _should_close_fp: bool = attr.ib(default=False, init=False)
+
+ def close(self) -> None:
"""
Close this reader/writer.
@@ -70,27 +143,30 @@
if self._closed:
return
self._closed = True
- if self._should_close_fp:
+ if self._fp is not None and self._should_close_fp:
self._fp.close()
- def __repr__(self):
- name = getattr(self._fp, "name", None)
- if name:
- wrapping = repr(name)
- else:
- wrapping = "<{} at 0x{:x}>".format(type(self._fp).__name__, id(self._fp))
- return "<jsonlines.{} at 0x{:x} wrapping {}>".format(
- type(self).__name__, id(self), wrapping
- )
+ def __repr__(self) -> str:
+ cls_name = type(self).__name__
+ wrapped = self._repr_for_wrapped()
+ return f"<jsonlines.{cls_name} at 0x{id(self):x} wrapping {wrapped}>"
- def __enter__(self):
+ def _repr_for_wrapped(self) -> str:
+ raise NotImplementedError # pragma: no cover
+
+ def __enter__(self: TRW) -> TRW:
return self
- def __exit__(self, *exc_info):
+ def __exit__(
+ self,
+ exc_type: Optional[Type[BaseException]],
+ exc_val: Optional[BaseException],
+ exc_tb: Optional[types.TracebackType],
+ ) -> None:
self.close()
- return False
+(a)attr.s(auto_attribs=True, repr=False)
class Reader(ReaderWriterBase):
"""
Reader for the jsonlines format.
@@ -100,33 +176,100 @@
an open file or an ``io.TextIO`` instance, but it can also be
something else as long as it yields strings when iterated over.
+ Instances are iterable and can be used as a context manager.
+
The `loads` argument can be used to replace the standard json
decoder. If specified, it must be a callable that accepts a
(unicode) string and returns the decoded object.
- Instances are iterable and can be used as a context manager.
-
- :param file-like iterable: iterable yielding lines as strings
- :param callable loads: custom json decoder callable
- """
+ :param file_or_iterable: file-like object or iterable yielding lines as
+ strings
+ :param loads: custom json decoder callable
+ """
+
+ _file_or_iterable: Union[
+ typing.IO[str], typing.IO[bytes], Iterable[Union[str, bytes]]
+ ]
+ _line_iter: Iterator[Tuple[int, Union[bytes, str]]] = attr.ib(init=False)
+ _loads: LoadsCallable = attr.ib(default=default_loads, kw_only=True)
+
+ def __attrs_post_init__(self) -> None:
+ if isinstance(self._file_or_iterable, io.IOBase):
+ self._fp = cast(
+ Union[typing.IO[str], typing.IO[bytes]],
+ self._file_or_iterable,
+ )
- def __init__(self, iterable, loads=None):
- self._fp = iterable
- self._should_close_fp = False
- self._closed = False
- if loads is None:
- loads = json.loads
- self._loads = loads
- self._line_iter = enumerate(iterable, 1)
+ self._line_iter = enumerate(self._file_or_iterable, 1)
- def read(self, type=None, allow_none=False, skip_empty=False):
+ # No type specified, None not allowed
+ @overload
+ def read(
+ self,
+ *,
+ type: Literal[None] = ...,
+ allow_none: Literal[False] = ...,
+ skip_empty: bool = ...,
+ ) -> JSONValue:
+ ... # pragma: no cover
+
+ # No type specified, None allowed
+ @overload
+ def read(
+ self,
+ *,
+ type: Literal[None] = ...,
+ allow_none: Literal[True],
+ skip_empty: bool = ...,
+ ) -> Optional[JSONValue]:
+ ... # pragma: no cover
+
+ # Type specified, None not allowed
+ @overload
+ def read(
+ self,
+ *,
+ type: Type[TJSONValue],
+ allow_none: Literal[False] = ...,
+ skip_empty: bool = ...,
+ ) -> TJSONValue:
+ ... # pragma: no cover
+
+ # Type specified, None allowed
+ @overload
+ def read(
+ self,
+ *,
+ type: Type[TJSONValue],
+ allow_none: Literal[True],
+ skip_empty: bool = ...,
+ ) -> Optional[TJSONValue]:
+ ... # pragma: no cover
+
+ # Generic definition
+ @overload
+ def read(
+ self,
+ *,
+ type: Optional[Type[Any]] = ...,
+ allow_none: bool = ...,
+ skip_empty: bool = ...,
+ ) -> Optional[JSONValue]:
+ ... # pragma: no cover
+
+ def read(
+ self,
+ *,
+ type: Optional[Type[Any]] = None,
+ allow_none: bool = False,
+ skip_empty: bool = False,
+ ) -> Optional[JSONValue]:
"""
Read and decode a line.
The optional `type` argument specifies the expected data type.
Supported types are ``dict``, ``list``, ``str``, ``int``,
- ``float``, ``numbers.Number`` (accepts both integers and
- floats), and ``bool``. When specified, non-conforming lines
+ ``float``, and ``bool``. When specified, non-conforming lines
result in :py:exc:`InvalidLineError`.
By default, input lines containing ``null`` (in JSON) are
@@ -158,8 +301,11 @@
)
raise exc from orig_exc
+ if line.startswith(SKIPPABLE_SINGLE_INITIAL_CHARS):
+ line = line[1:]
+
try:
- value = self._loads(line)
+ value: JSONValue = self._loads(line)
except ValueError as orig_exc:
exc = InvalidLineError(
f"line contains invalid json: {orig_exc}", line, lineno
@@ -173,8 +319,9 @@
if type is not None:
valid = isinstance(value, type)
- if type in (int, numbers.Number):
- valid = valid and not isinstance(value, bool)
+ if type is int and isinstance(value, bool):
+ # isinstance() is not sufficient, since bool is an int subclass
+ valid = False
if not valid:
raise InvalidLineError(
"line does not match requested type", line, lineno
@@ -182,7 +329,73 @@
return value
- def iter(self, type=None, allow_none=False, skip_empty=False, skip_invalid=False):
+ # No type specified, None not allowed
+ @overload
+ def iter(
+ self,
+ *,
+ type: Literal[None] = ...,
+ allow_none: Literal[False] = ...,
+ skip_empty: bool = ...,
+ skip_invalid: bool = ...,
+ ) -> Iterator[JSONValue]:
+ ... # pragma: no cover
+
+ # No type specified, None allowed
+ @overload
+ def iter(
+ self,
+ *,
+ type: Literal[None] = ...,
+ allow_none: Literal[True],
+ skip_empty: bool = ...,
+ skip_invalid: bool = ...,
+ ) -> Iterator[JSONValue]:
+ ... # pragma: no cover
+
+ # Type specified, None not allowed
+ @overload
+ def iter(
+ self,
+ *,
+ type: Type[TJSONValue],
+ allow_none: Literal[False] = ...,
+ skip_empty: bool = ...,
+ skip_invalid: bool = ...,
+ ) -> Iterator[TJSONValue]:
+ ... # pragma: no cover
+
+ # Type specified, None allowed
+ @overload
+ def iter(
+ self,
+ *,
+ type: Type[TJSONValue],
+ allow_none: Literal[True],
+ skip_empty: bool = ...,
+ skip_invalid: bool = ...,
+ ) -> Iterator[Optional[TJSONValue]]:
+ ... # pragma: no cover
+
+ # Generic definition
+ @overload
+ def iter(
+ self,
+ *,
+ type: Optional[Type[TJSONValue]] = ...,
+ allow_none: bool = ...,
+ skip_empty: bool = ...,
+ skip_invalid: bool = ...,
+ ) -> Iterator[Optional[TJSONValue]]:
+ ... # pragma: no cover
+
+ def iter(
+ self,
+ type: Optional[Type[Any]] = None,
+ allow_none: bool = False,
+ skip_empty: bool = False,
+ skip_invalid: bool = False,
+ ) -> Iterator[Optional[JSONValue]]:
"""
Iterate over all lines.
@@ -209,17 +422,26 @@
except EOFError:
pass
- def __iter__(self):
+ def __iter__(self) -> Iterator[Any]:
"""
See :py:meth:`~Reader.iter()`.
"""
return self.iter()
+ def _repr_for_wrapped(self) -> str:
+ if self._fp is not None:
+ return repr_for_fp(self._fp)
+ class_name = type(self._file_or_iterable).__name__
+ return f"<{class_name} at 0x{id(self._file_or_iterable):x}>"
+
+(a)attr.s(auto_attribs=True, repr=False)
class Writer(ReaderWriterBase):
"""
Writer for the jsonlines format.
+ Instances can be used as a context manager.
+
The `fp` argument must be a file-like object with a ``.write()``
method accepting either text (unicode) or bytes.
@@ -235,63 +457,144 @@
When the `flush` argument is set to ``True``, the writer will call
``fp.flush()`` after each written line.
- Instances can be used as a context manager.
-
- :param file-like fp: writable file-like object
- :param bool compact: whether to use a compact output format
- :param bool sort_keys: whether to sort object keys
- :param callable dumps: custom encoder callable
- :param bool flush: whether to flush the file-like object after
- writing each line
- """
+ :param fp: writable file-like object
+ :param compact: whether to use a compact output format
+ :param sort_keys: whether to sort object keys
+ :param dumps: custom encoder callable
+ :param flush: whether to flush the file-like object after writing each line
+ """
+
+ _fp: Union[typing.IO[str], typing.IO[bytes]] = attr.ib(default=None)
+ _fp_is_binary: bool = attr.ib(default=False, init=False)
+ _compact: bool = attr.ib(default=False, kw_only=True)
+ _sort_keys: bool = attr.ib(default=False, kw_only=True)
+ _flush: bool = attr.ib(default=False, kw_only=True)
+ _dumps: DumpsCallable = attr.ib(default=default_dumps, kw_only=True)
+ _dumps_result_conversion: DumpsResultConversion = attr.ib(
+ default=DumpsResultConversion.LeaveAsIs, init=False
+ )
- def __init__(self, fp, compact=False, sort_keys=False, dumps=None, flush=False):
- self._closed = False
- try:
- fp.write("")
+ def __attrs_post_init__(self) -> None:
+ if isinstance(self._fp, io.TextIOBase):
self._fp_is_binary = False
- except TypeError:
+ elif isinstance(self._fp, io.IOBase):
self._fp_is_binary = True
- if dumps is None:
- encoder_kwargs = dict(ensure_ascii=False, sort_keys=sort_keys)
- if compact:
+ else:
+ try:
+ self._fp.write("") # type: ignore[arg-type]
+ except TypeError:
+ self._fp_is_binary = True
+ else:
+ self._fp_is_binary = False
+
+ if self._dumps is default_dumps:
+ encoder_kwargs: Dict[str, Any] = dict(
+ ensure_ascii=False,
+ sort_keys=self._sort_keys,
+ )
+ if self._compact:
encoder_kwargs.update(separators=(",", ":"))
- dumps = json.JSONEncoder(**encoder_kwargs).encode
- self._fp = fp
- self._should_close_fp = False
- self._dumps = dumps
- self._flush = flush
+ self._dumps = json.JSONEncoder(**encoder_kwargs).encode
- def write(self, obj):
+ # Detect if str-to-bytes conversion (or vice versa) is needed for the
+ # combination of this file-like object and the used dumps() callable.
+ # This avoids checking this for each .write(). Note that this
+ # deliberately does not support ���dynamic��� return types that depend on
+ # input and dump options, like simplejson on Python 2 in some cases.
+ sample_dumps_result = self._dumps({})
+ if isinstance(sample_dumps_result, str) and self._fp_is_binary:
+ self._dumps_result_conversion = DumpsResultConversion.EncodeToBytes
+ elif isinstance(sample_dumps_result, bytes) and not self._fp_is_binary:
+ self._dumps_result_conversion = DumpsResultConversion.DecodeToString
+
+ def write(self, obj: Any) -> int:
"""
Encode and write a single object.
:param obj: the object to encode and write
+ :return: number of characters or bytes written
"""
if self._closed:
raise RuntimeError("writer is closed")
+
line = self._dumps(obj)
- if self._fp_is_binary:
- line = line.encode("utf-8")
- self._fp.write(line)
- self._fp.write(b"\n")
- else:
- self._fp.write(line)
- self._fp.write("\n")
+
+ # This handles either str or bytes, but the type checker does not know
+ # that this code always passes the right type of arguments.
+ if self._dumps_result_conversion == DumpsResultConversion.EncodeToBytes:
+ line = line.encode() # type: ignore[union-attr]
+ elif self._dumps_result_conversion == DumpsResultConversion.DecodeToString:
+ line = line.decode() # type: ignore[union-attr]
+
+ fp = self._fp
+ fp.write(line) # type: ignore[arg-type]
+ fp.write(b"\n" if self._fp_is_binary else "\n") # type: ignore[arg-type]
+
if self._flush:
- self._fp.flush()
+ fp.flush()
+
+ return len(line) + 1 # including newline
- def write_all(self, iterable):
+ def write_all(self, iterable: Iterable[Any]) -> int:
"""
Encode and write multiple objects.
:param iterable: an iterable of objects
+ :return: number of characters or bytes written
"""
- for obj in iterable:
- self.write(obj)
+ return sum(self.write(obj) for obj in iterable)
+ def _repr_for_wrapped(self) -> str:
+ return repr_for_fp(self._fp)
-def open(name, mode="r", **kwargs):
+
+@overload
+def open(
+ file: Openable,
+ mode: Literal["r"] = ...,
+ *,
+ loads: Optional[LoadsCallable] = ...,
+) -> Reader:
+ ... # pragma: no cover
+
+
+@overload
+def open(
+ file: Openable,
+ mode: Literal["w", "a", "x"],
+ *,
+ dumps: Optional[DumpsCallable] = ...,
+ compact: Optional[bool] = ...,
+ sort_keys: Optional[bool] = ...,
+ flush: Optional[bool] = ...,
+) -> Writer:
+ ... # pragma: no cover
+
+
+@overload
+def open(
+ file: Openable,
+ mode: str = ...,
+ *,
+ loads: Optional[LoadsCallable] = ...,
+ dumps: Optional[DumpsCallable] = ...,
+ compact: Optional[bool] = ...,
+ sort_keys: Optional[bool] = ...,
+ flush: Optional[bool] = ...,
+) -> Union[Reader, Writer]:
+ ... # pragma: no cover
+
+
+def open(
+ file: Openable,
+ mode: str = "r",
+ *,
+ loads: Optional[LoadsCallable] = None,
+ dumps: Optional[DumpsCallable] = None,
+ compact: Optional[bool] = None,
+ sort_keys: Optional[bool] = None,
+ flush: Optional[bool] = None,
+) -> Union[Reader, Writer]:
"""
Open a jsonlines file for reading or writing.
@@ -312,17 +615,35 @@
with jsonlines.open('out.jsonl', mode='w') as writer:
writer.write(...)
- :param file-like fp: name of the file to open
- :param str mode: whether to open the file for reading (``r``),
+ :param file: name or ���path-like object��� of the file to open
+ :param mode: whether to open the file for reading (``r``),
writing (``w``) or appending (``a``).
- :param **kwargs: additional arguments, forwarded to the reader or writer
"""
- if mode not in {"r", "w", "a"}:
- raise ValueError("'mode' must be either 'r', 'w', or 'a'")
- fp = builtins.open(name, mode=mode + "t", encoding="utf-8")
- if mode == "r":
- instance = Reader(fp, **kwargs)
- else:
- instance = Writer(fp, **kwargs)
+ if mode not in {"r", "w", "a", "x"}:
+ raise ValueError("'mode' must be either 'r', 'w', 'a', or 'x'")
+
+ cls = Reader if mode == "r" else Writer
+ encoding = "utf-8-sig" if mode == "r" else "utf-8"
+ fp = builtins.open(file, mode=mode + "t", encoding=encoding)
+ kwargs = dict(
+ loads=loads,
+ dumps=dumps,
+ compact=compact,
+ sort_keys=sort_keys,
+ flush=flush,
+ )
+ kwargs = {key: value for key, value in kwargs.items() if value is not None}
+ instance: Union[Reader, Writer] = cls(fp, **kwargs)
instance._should_close_fp = True
return instance
+
+
+def repr_for_fp(fp: typing.IO[Any]) -> str:
+ """
+ Helper to make a useful repr() for a file-like object.
+ """
+ name = getattr(fp, "name", None)
+ if name is not None:
+ return repr(name)
+ else:
+ return repr(fp)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonlines-2.0.0/mypy.ini new/jsonlines-3.1.0/mypy.ini
--- old/jsonlines-2.0.0/mypy.ini 1970-01-01 01:00:00.000000000 +0100
+++ new/jsonlines-3.1.0/mypy.ini 2022-07-01 18:35:18.000000000 +0200
@@ -0,0 +1,16 @@
+[mypy]
+check_untyped_defs = True
+disallow_any_generics = True
+disallow_incomplete_defs = True
+disallow_subclassing_any = True
+disallow_untyped_calls = True
+disallow_untyped_decorators = True
+disallow_untyped_defs = True
+no_implicit_optional = True
+no_implicit_reexport = True
+show_error_codes = True
+strict_equality = True
+warn_redundant_casts = True
+warn_return_any = True
+warn_unused_configs = True
+warn_unused_ignores = True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonlines-2.0.0/requirements-dev.txt new/jsonlines-3.1.0/requirements-dev.txt
--- old/jsonlines-2.0.0/requirements-dev.txt 2021-01-04 17:03:18.000000000 +0100
+++ new/jsonlines-3.1.0/requirements-dev.txt 2022-07-01 18:35:18.000000000 +0200
@@ -1,5 +1,6 @@
black
flake8
+mypy
pytest>=3
pytest-cov
sphinx
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonlines-2.0.0/setup.cfg new/jsonlines-3.1.0/setup.cfg
--- old/jsonlines-2.0.0/setup.cfg 2021-01-04 17:03:18.000000000 +0100
+++ new/jsonlines-3.1.0/setup.cfg 2022-07-01 18:35:18.000000000 +0200
@@ -1,7 +1,7 @@
[metadata]
name = jsonlines
-version = 2.0.0
-author = Wouter Bolsterlee
+version = 3.1.0
+author = wouter bolsterlee
author_email = wouter(a)bolsterl.ee
license = BSD
license_file = LICENSE.rst
@@ -24,6 +24,12 @@
[options]
packages = jsonlines
python_requires = >=3.6
+install_requires =
+ attrs>=19.2.0
+ typing_extensions; python_version < "3.8"
+
+[options.package_data]
+jsonlines = py.typed
[build_sphinx]
source-dir = doc/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonlines-2.0.0/tests/sample-crlf-line-separators.jsonl new/jsonlines-3.1.0/tests/sample-crlf-line-separators.jsonl
--- old/jsonlines-2.0.0/tests/sample-crlf-line-separators.jsonl 2021-01-04 17:03:18.000000000 +0100
+++ new/jsonlines-3.1.0/tests/sample-crlf-line-separators.jsonl 1970-01-01 01:00:00.000000000 +0100
@@ -1,2 +0,0 @@
-{"a": 1}
-{"b": 2}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonlines-2.0.0/tests/sample-no-eol-at-eof.jsonl new/jsonlines-3.1.0/tests/sample-no-eol-at-eof.jsonl
--- old/jsonlines-2.0.0/tests/sample-no-eol-at-eof.jsonl 2021-01-04 17:03:18.000000000 +0100
+++ new/jsonlines-3.1.0/tests/sample-no-eol-at-eof.jsonl 1970-01-01 01:00:00.000000000 +0100
@@ -1,2 +0,0 @@
-{"a": 1}
-{"b": 2}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonlines-2.0.0/tests/sample.jsonl new/jsonlines-3.1.0/tests/sample.jsonl
--- old/jsonlines-2.0.0/tests/sample.jsonl 2021-01-04 17:03:18.000000000 +0100
+++ new/jsonlines-3.1.0/tests/sample.jsonl 1970-01-01 01:00:00.000000000 +0100
@@ -1,2 +0,0 @@
-{"a": 1}
-{"b": 2}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonlines-2.0.0/tests/test_jsonlines.py new/jsonlines-3.1.0/tests/test_jsonlines.py
--- old/jsonlines-2.0.0/tests/test_jsonlines.py 2021-01-04 17:03:18.000000000 +0100
+++ new/jsonlines-3.1.0/tests/test_jsonlines.py 2022-07-01 18:35:18.000000000 +0200
@@ -2,11 +2,13 @@
Tests for the jsonlines library.
"""
+import codecs
import collections
import io
-import jsonlines
+import json
import tempfile
+import jsonlines
import pytest
@@ -14,7 +16,7 @@
SAMPLE_TEXT = SAMPLE_BYTES.decode("utf-8")
-def test_reader():
+def test_reader() -> None:
fp = io.BytesIO(SAMPLE_BYTES)
with jsonlines.Reader(fp) as reader:
it = iter(reader)
@@ -26,13 +28,61 @@
reader.read()
-def test_reading_from_iterable():
+def test_reading_from_iterable() -> None:
with jsonlines.Reader(["1", b"{}"]) as reader:
assert list(reader) == [1, {}]
assert "wrapping <list at " in repr(reader)
-def test_writer_text():
+def test_reader_rfc7464_text_sequences() -> None:
+ fp = io.BytesIO(b'\x1e"a"\x0a\x1e"b"\x0a')
+ with jsonlines.Reader(fp) as reader:
+ assert list(reader) == ["a", "b"]
+
+
+def test_reader_utf8_bom_bytes() -> None:
+ """
+ UTF-8 BOM is ignored, even if it occurs in the middle of a stream.
+ """
+ chunks = [
+ codecs.BOM_UTF8,
+ b"1\n",
+ codecs.BOM_UTF8,
+ b"2\n",
+ ]
+ fp = io.BytesIO(b"".join(chunks))
+ with jsonlines.Reader(fp) as reader:
+ assert list(reader) == [1, 2]
+
+
+def test_reader_utf8_bom_text() -> None:
+ """
+ Text version of ``test_reader_utf8_bom_bytes()``.
+ """
+ chunks = [
+ "1\n",
+ codecs.BOM_UTF8.decode(),
+ "2\n",
+ ]
+ fp = io.StringIO("".join(chunks))
+ with jsonlines.Reader(fp) as reader:
+ assert list(reader) == [1, 2]
+
+
+def test_reader_utf8_bom_bom_bom() -> None:
+ """
+ Too many UTF-8 BOM BOM BOM chars cause BOOM ���� BOOM.
+ """
+ reader = jsonlines.Reader([codecs.BOM_UTF8.decode() * 3 + "1\n"])
+ with pytest.raises(jsonlines.InvalidLineError) as excinfo:
+ reader.read()
+
+ exc = excinfo.value
+ assert "invalid json" in str(exc)
+ assert isinstance(exc.__cause__, json.JSONDecodeError)
+
+
+def test_writer_text() -> None:
fp = io.StringIO()
with jsonlines.Writer(fp) as writer:
writer.write({"a": 1})
@@ -40,7 +90,7 @@
assert fp.getvalue() == SAMPLE_TEXT
-def test_writer_binary():
+def test_writer_binary() -> None:
fp = io.BytesIO()
with jsonlines.Writer(fp) as writer:
writer.write_all(
@@ -52,7 +102,7 @@
assert fp.getvalue() == SAMPLE_BYTES
-def test_closing():
+def test_closing() -> None:
reader = jsonlines.Reader([])
reader.close()
with pytest.raises(RuntimeError):
@@ -64,7 +114,7 @@
writer.write(123)
-def test_invalid_lines():
+def test_invalid_lines() -> None:
data = "[1, 2"
with jsonlines.Reader(io.StringIO(data)) as reader:
with pytest.raises(jsonlines.InvalidLineError) as excinfo:
@@ -72,9 +122,10 @@
exc = excinfo.value
assert "invalid json" in str(exc)
assert exc.line == data
+ assert isinstance(exc.__cause__, json.JSONDecodeError)
-def test_skip_invalid():
+def test_skip_invalid() -> None:
fp = io.StringIO("12\ninvalid\n34")
reader = jsonlines.Reader(fp)
it = reader.iter(skip_invalid=True)
@@ -82,7 +133,7 @@
assert next(it) == 34
-def test_empty_strings_in_iterable():
+def test_empty_strings_in_iterable() -> None:
input = ["123", "", "456"]
it = iter(jsonlines.Reader(input))
assert next(it) == 123
@@ -94,14 +145,14 @@
assert list(it) == [123, 456]
-def test_invalid_utf8():
+def test_invalid_utf8() -> None:
with jsonlines.Reader([b"\xff\xff"]) as reader:
with pytest.raises(jsonlines.InvalidLineError) as excinfo:
reader.read()
assert "line is not valid utf-8" in str(excinfo.value)
-def test_empty_lines():
+def test_empty_lines() -> None:
data_with_empty_line = b"1\n\n2\n"
with jsonlines.Reader(io.BytesIO(data_with_empty_line)) as reader:
assert reader.read()
@@ -114,9 +165,16 @@
assert list(reader.iter(skip_empty=True)) == [1, 2]
-def test_typed_reads():
- with jsonlines.Reader(io.StringIO('12\n"foo"\n')) as reader:
+def test_typed_reads() -> None:
+ with jsonlines.Reader(io.StringIO('12\ntrue\n"foo"\n')) as reader:
assert reader.read(type=int) == 12
+
+ with pytest.raises(jsonlines.InvalidLineError) as excinfo:
+ reader.read(type=int)
+ exc = excinfo.value
+ assert "does not match requested type" in str(exc)
+ assert exc.line == 'true'
+
with pytest.raises(jsonlines.InvalidLineError) as excinfo:
reader.read(type=float)
exc = excinfo.value
@@ -124,7 +182,15 @@
assert exc.line == '"foo"'
-def test_typed_iteration():
+def test_typed_read_invalid_type() -> None:
+ reader = jsonlines.Reader([])
+ with pytest.raises(ValueError) as excinfo:
+ reader.read(type="nope") # type: ignore[call-overload]
+ exc = excinfo.value
+ assert str(exc) == "invalid type specified"
+
+
+def test_typed_iteration() -> None:
fp = io.StringIO("1\n2\n")
with jsonlines.Reader(fp) as reader:
actual = list(reader.iter(type=int))
@@ -139,7 +205,7 @@
assert "does not match requested type" in str(exc)
-def test_writer_flags():
+def test_writer_flags() -> None:
fp = io.BytesIO()
with jsonlines.Writer(fp, compact=True, sort_keys=True) as writer:
writer.write(
@@ -153,21 +219,36 @@
assert fp.getvalue() == b'{"a":1,"b":2}\n'
-def test_custom_dumps():
+def test_custom_dumps() -> None:
fp = io.BytesIO()
writer = jsonlines.Writer(fp, dumps=lambda obj: "oh hai")
with writer:
- writer.write({})
+ nbytes = writer.write({})
+ assert nbytes == len(b"oh hai\n")
+
assert fp.getvalue() == b"oh hai\n"
-def test_custom_loads():
+def test_custom_dumps_bytes() -> None:
+ """
+ A custom dump function that returns bytes (e.g. ���orjson���) should work.
+ """
+
+ fp = io.BytesIO()
+ writer = jsonlines.Writer(fp, dumps=lambda obj: b"some bytes")
+ with writer:
+ writer.write(123)
+
+ assert fp.getvalue() == b"some bytes\n"
+
+
+def test_custom_loads() -> None:
fp = io.BytesIO(b"{}\n")
with jsonlines.Reader(fp, loads=lambda s: "uh what") as reader:
assert reader.read() == "uh what"
-def test_open_reading():
+def test_open_reading() -> None:
with tempfile.NamedTemporaryFile("wb") as fp:
fp.write(b"123\n")
fp.flush()
@@ -175,7 +256,19 @@
assert list(reader) == [123]
-def test_open_writing():
+def test_open_reading_with_utf8_bom() -> None:
+ """
+ The ``.open()`` helper ignores a UTF-8 BOM.
+ """
+ with tempfile.NamedTemporaryFile("wb") as fp:
+ fp.write(codecs.BOM_UTF8)
+ fp.write(b"123\n")
+ fp.flush()
+ with jsonlines.open(fp.name) as reader:
+ assert list(reader) == [123]
+
+
+def test_open_writing() -> None:
with tempfile.NamedTemporaryFile("w+b") as fp:
with jsonlines.open(fp.name, mode="w") as writer:
writer.write(123)
@@ -183,16 +276,25 @@
assert fp.name in repr(writer)
-def test_open_and_append_writing():
+def test_open_and_append_writing() -> None:
with tempfile.NamedTemporaryFile("w+b") as fp:
with jsonlines.open(fp.name, mode="w") as writer:
- writer.write(123)
+ nbytes = writer.write(123)
+ assert nbytes == len(str(123)) + 1
with jsonlines.open(fp.name, mode="a") as writer:
- writer.write(456)
+ nbytes = writer.write(456)
+ assert nbytes == len(str(456)) + 1
assert fp.read() == b"123\n456\n"
-def test_open_invalid_mode():
+def test_open_invalid_mode() -> None:
with pytest.raises(ValueError) as excinfo:
jsonlines.open("foo", mode="foo")
assert "mode" in str(excinfo.value)
+
+
+def test_single_char_stripping() -> None:
+ """ "
+ Sanity check that a helper constant actually contains single-char strings.
+ """
+ assert all(len(s) == 1 for s in jsonlines.jsonlines.SKIPPABLE_SINGLE_INITIAL_CHARS)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonlines-2.0.0/tests/test_typing.py new/jsonlines-3.1.0/tests/test_typing.py
--- old/jsonlines-2.0.0/tests/test_typing.py 1970-01-01 01:00:00.000000000 +0100
+++ new/jsonlines-3.1.0/tests/test_typing.py 2022-07-01 18:35:18.000000000 +0200
@@ -0,0 +1,96 @@
+"""
+This file should give any type checking errors.
+"""
+
+import io
+import json
+import random
+
+import numbers
+from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional
+
+
+if not TYPE_CHECKING:
+
+ def reveal_type(obj: Any) -> None:
+ pass
+
+
+import jsonlines
+
+
+def something_with_reader() -> None:
+
+ reader: jsonlines.Reader
+ reader = jsonlines.Reader(io.StringIO())
+ reader = jsonlines.Reader(io.BytesIO())
+ reader = jsonlines.Reader(['"text"'])
+ reader = jsonlines.Reader([b'"bytes"'])
+
+ r1 = reader.read()
+ r2 = reader.read(allow_none=True)
+ r3: numbers.Number = reader.read(type=random.choice([int, float]))
+
+ # For debugging:
+ # reveal_type(r1)
+ # reveal_type(r2)
+ # reveal_type(r3)
+
+ some_int: int = reader.read(type=int)
+ maybe_int: Optional[int] = reader.read(type=int, allow_none=True)
+
+ some_float: float = reader.read(type=float)
+ maybe_float: Optional[float] = reader.read(type=float, allow_none=True)
+
+ some_bool: bool = reader.read(type=bool)
+ maybe_bool: Optional[bool] = reader.read(type=bool, allow_none=True)
+
+ some_dict: Dict[str, Any] = reader.read(type=dict)
+ optional_dict: Optional[Dict[str, Any]] = reader.read(type=dict, allow_none=True)
+
+ some_list: List[Any] = reader.read(type=list)
+ maybe_list: Optional[List[Any]] = reader.read(type=list, allow_none=True)
+
+ iter_int: Iterable[int] = reader.iter(type=int)
+ iter_str: Iterable[str] = reader.iter(type=str)
+ iter_dict: Iterable[Dict[str, Any]] = reader.iter(type=dict)
+ iter_optional_str: Iterable[Optional[str]] = reader.iter(type=str, allow_none=True)
+
+ locals() # Silence flake8 F841
+
+
+def something_with_writer() -> None:
+ writer: jsonlines.Writer
+ writer = jsonlines.Writer(io.StringIO())
+ writer = jsonlines.Writer(io.BytesIO())
+
+ locals() # Silence flake8 F841
+
+
+def something_with_open() -> None:
+ name = "/nonexistent"
+
+ reader: jsonlines.Reader
+ reader = jsonlines.open(name)
+ reader = jsonlines.open(name, "r")
+ reader = jsonlines.open(name, mode="r")
+ reader = jsonlines.open(
+ name,
+ mode="r",
+ loads=json.loads,
+ )
+
+ writer: jsonlines.Writer
+ writer = jsonlines.open(name, "w")
+ writer = jsonlines.open(name, mode="w")
+ writer = jsonlines.open(name, "a")
+ writer = jsonlines.open(
+ name,
+ mode="w",
+ dumps=json.dumps,
+ compact=True,
+ sort_keys=True,
+ flush=True,
+ )
+
+ locals() # Silence flake8 F841
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jsonlines-2.0.0/tox.ini new/jsonlines-3.1.0/tox.ini
--- old/jsonlines-2.0.0/tox.ini 2021-01-04 17:03:18.000000000 +0100
+++ new/jsonlines-3.1.0/tox.ini 2022-07-01 18:35:18.000000000 +0200
@@ -1,5 +1,5 @@
[tox]
-envlist = py36,py37,py38,py39,flake8
+envlist = py310,py39,py38,py37,py36,linters
[testenv]
deps = -rrequirements-dev.txt
@@ -11,3 +11,4 @@
commands =
flake8 jsonlines/ tests/
black --check jsonlines/ tests/
+ mypy --strict jsonlines/ tests/
1
0
Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-jsonslicer for openSUSE:Factory checked in at 2022-09-30 17:57:55
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-jsonslicer (Old)
and /work/SRC/openSUSE:Factory/.python-jsonslicer.new.2275 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-jsonslicer"
Fri Sep 30 17:57:55 2022 rev:5 rq:1007078 version:0.1.7
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-jsonslicer/python-jsonslicer.changes 2021-06-01 10:36:18.644695471 +0200
+++ /work/SRC/openSUSE:Factory/.python-jsonslicer.new.2275/python-jsonslicer.changes 2022-09-30 17:58:14.737306479 +0200
@@ -1,0 +2,6 @@
+Thu Sep 29 14:22:11 UTC 2022 - Yogalakshmi Arunachalam <yarunachalam(a)suse.com>
+
+- Update to version 0.1.7
+ * Make YAJL error verbosity tunable
+
+-------------------------------------------------------------------
Old:
----
jsonslicer-0.1.6.tar.gz
New:
----
jsonslicer-0.1.7.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-jsonslicer.spec ++++++
--- /var/tmp/diff_new_pack.tksuqF/_old 2022-09-30 17:58:15.169307402 +0200
+++ /var/tmp/diff_new_pack.tksuqF/_new 2022-09-30 17:58:15.173307410 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-jsonslicer
#
-# Copyright (c) 2021 SUSE LLC
+# Copyright (c) 2022 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%define skip_python2 1
Name: python-jsonslicer
-Version: 0.1.6
+Version: 0.1.7
Release: 0
Summary: Streaming JSON parser with iterator interface
License: MIT
++++++ jsonslicer-0.1.6.tar.gz -> jsonslicer-0.1.7.tar.gz ++++++
++++ 1688 lines of diff (skipped)
1
0
Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-jsonrpclib-pelix for openSUSE:Factory checked in at 2022-09-30 17:57:56
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-jsonrpclib-pelix (Old)
and /work/SRC/openSUSE:Factory/.python-jsonrpclib-pelix.new.2275 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-jsonrpclib-pelix"
Fri Sep 30 17:57:56 2022 rev:4 rq:1007079 version:0.4.3.2
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-jsonrpclib-pelix/python-jsonrpclib-pelix.changes 2020-05-01 11:13:03.979810014 +0200
+++ /work/SRC/openSUSE:Factory/.python-jsonrpclib-pelix.new.2275/python-jsonrpclib-pelix.changes 2022-09-30 17:58:15.373307838 +0200
@@ -1,0 +2,36 @@
+Thu Sep 29 14:18:09 UTC 2022 - Yogalakshmi Arunachalam <yarunachalam(a)suse.com>
+
+- Update to v0.4.3.2
+ * Reordered PooledJSONRPCServer inheritance definition (#55)
+ * Migration of Continuous Integration:
+ * Use PyTest instead of Nose
+ * Run CI with GitHub Actions instead of Travis-CI
+
+- Update to v0.4.3.1
+ * Removed remaining print statements (#52)
+
+- Update to v0.4.3
+ * ServerProxy keeps the given query string, as before 0.4.2. This release fixes #51, and a unit test has been added to ensure
+ there won't be any regression again on this feature
+ * JSON library selection is now made in the jsonrpclib.jsonlib module, using a set of handler classes. This will ease the addition of new libraries.
+ * Added support for ujson
+ * Fixed Travis-CI builds (migrated from .org to .com and bypassed the coveralls issue with ppc64le)
+ * Fixed an issue with the CGI test in Python 3-only environments
+
+- Update to v0.4.2
+ * Use urlparse from urllib.parse (Python 3) or urlparse (Python 2)
+ * to prepare for the deprecation of urllib.parse.splittype.
+ * Thanks to @citrus-it and @markmcclain for this fix.
+ (see #44 and #45 for more details)
+ * Unix socket clients now send localhost as Host: HTTP field instead of
+ the path to the socket
+ (see #47).
+ * Thanks @markmcclain for this fix.
+ * Added a TransportError exception, subclass of ProtocolError, which
+ provides more details
+ (see #49).
+ Thanks @markmcclain for this improvement.
+ * Added PowerPC 64 architecture (ppc64le) to Travis CI runs, to ease the
+ * integration of new release into RHEL/Ubuntu (see #50 by @kishorkunal-raj)
+
+-------------------------------------------------------------------
Old:
----
v0.4.1.tar.gz
New:
----
v0.4.3.2.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-jsonrpclib-pelix.spec ++++++
--- /var/tmp/diff_new_pack.NofJ3o/_old 2022-09-30 17:58:15.833308821 +0200
+++ /var/tmp/diff_new_pack.NofJ3o/_new 2022-09-30 17:58:15.841308838 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-jsonrpclib-pelix
#
-# Copyright (c) 2020 SUSE LLC
+# Copyright (c) 2022 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-jsonrpclib-pelix
-Version: 0.4.1
+Version: 0.4.3.2
Release: 0
Summary: JSPN-RPC over HTTP Library for Pelix Remote Services
License: Apache-2.0
++++++ v0.4.1.tar.gz -> v0.4.3.2.tar.gz ++++++
++++ 4751 lines of diff (skipped)
1
0
Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-kiss-headers for openSUSE:Factory checked in at 2022-09-30 17:57:54
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-kiss-headers (Old)
and /work/SRC/openSUSE:Factory/.python-kiss-headers.new.2275 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-kiss-headers"
Fri Sep 30 17:57:54 2022 rev:3 rq:1007077 version:2.3.1
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-kiss-headers/python-kiss-headers.changes 2021-04-01 14:18:56.456126301 +0200
+++ /work/SRC/openSUSE:Factory/.python-kiss-headers.new.2275/python-kiss-headers.changes 2022-09-30 17:58:13.933304760 +0200
@@ -1,0 +2,8 @@
+Thu Sep 29 14:23:36 UTC 2022 - Yogalakshmi Arunachalam <yarunachalam(a)suse.com>
+
+- Update of Version 2.3.1
+ Changes:
+ * Explicit support for Python 3.11, migrate to GHA, and add _all_ to top-level init.
+ * Minor package refresh.
+
+-------------------------------------------------------------------
Old:
----
kiss-headers-2.3.0.tar.gz
New:
----
kiss-headers-2.3.1.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-kiss-headers.spec ++++++
--- /var/tmp/diff_new_pack.JOI5cS/_old 2022-09-30 17:58:14.509305991 +0200
+++ /var/tmp/diff_new_pack.JOI5cS/_new 2022-09-30 17:58:14.513305999 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-kiss-headers
#
-# Copyright (c) 2021 SUSE LLC
+# Copyright (c) 2022 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%define skip_python2 1
Name: python-kiss-headers
-Version: 2.3.0
+Version: 2.3.1
Release: 0
Summary: Python package for parsing HTTP/1.1 style headers to objects
License: MIT
++++++ kiss-headers-2.3.0.tar.gz -> kiss-headers-2.3.1.tar.gz ++++++
++++ 7039 lines of diff (skipped)
1
0
Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-lesscpy for openSUSE:Factory checked in at 2022-09-30 17:57:53
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-lesscpy (Old)
and /work/SRC/openSUSE:Factory/.python-lesscpy.new.2275 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-lesscpy"
Fri Sep 30 17:57:53 2022 rev:12 rq:1007074 version:0.15.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-lesscpy/python-lesscpy.changes 2020-04-15 19:57:00.157686155 +0200
+++ /work/SRC/openSUSE:Factory/.python-lesscpy.new.2275/python-lesscpy.changes 2022-09-30 17:58:12.553301810 +0200
@@ -1,0 +2,34 @@
+Thu Sep 29 14:42:56 UTC 2022 - Yogalakshmi Arunachalam <yarunachalam(a)suse.com>
+
+- Release version 0.15.0
+ * Update .gitignore
+ * Remove old interpreter versions
+ * Merge pull request #118 from lephe/master
+ * Improve support for nested media queries
+ * Merge pull request #117 from lephe/master
+ * Basic support for CSS4 variables
+ * Merge pull request #109 from saschpe/rel
+
+- Release version 0.14.0
+ * Merge pull request #108 from saschpe/pycharm
+ * Version a PyCharm project configuration
+ * Update .gitignore rules based on Github Boilerplate
+ * Merge pull request #107 from saschpe/cleanups
+ * Add Python-3.7 to supported versions
+ * Improve minor style issues
+ * Merge pull request #105 from tirkarthi/fix-collections-import
+ * Import ABC from collections.abc instead of collections for Python 3.9 compatibility.
+ * Merge pull request #99 from hroncok/rpartition
+ * Merge pull request #98 from movermeyer/fix_badges
+ * Merge pull request #100 from cladmi/pr/fix/readme/command
+ * README.rst: fix the outdated lesscpy usage
+ * Apply PEP-8 consistently
+ * Allow to run tests from versioned folder
+ * Switched broken pypip.in badges to shields.io
+ * Merge pull request #96 from sambler/pyversions
+ * Merge pull request #97 from sambler/pysix
+ * adjust tox.ini to prevent errors from missing interpreters
+ * Make use of six.string_types consistent
+ * update python versions 2.6 was EOL in 2013 3.3 was EOL in 2017-09 add 3.6, pypy and pypy3
+
+-------------------------------------------------------------------
Old:
----
lesscpy-0.14.0.tar.gz
New:
----
lesscpy-0.15.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-lesscpy.spec ++++++
--- /var/tmp/diff_new_pack.uxY746/_old 2022-09-30 17:58:12.981302724 +0200
+++ /var/tmp/diff_new_pack.uxY746/_new 2022-09-30 17:58:12.985302733 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-lesscpy
#
-# Copyright (c) 2020 SUSE LLC
+# Copyright (c) 2022 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-lesscpy
-Version: 0.14.0
+Version: 0.15.0
Release: 0
Summary: Lesscss compiler
License: MIT
@@ -34,7 +34,7 @@
Requires: python-setuptools
Requires: python-six
Requires(post): update-alternatives
-Requires(postun): update-alternatives
+Requires(postun):update-alternatives
BuildArch: noarch
%python_subpackages
++++++ lesscpy-0.14.0.tar.gz -> lesscpy-0.15.0.tar.gz ++++++
++++ 1762 lines of diff (skipped)
1
0
Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-labels for openSUSE:Factory checked in at 2022-09-30 17:57:54
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-labels (Old)
and /work/SRC/openSUSE:Factory/.python-labels.new.2275 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-labels"
Fri Sep 30 17:57:54 2022 rev:3 rq:1007075 version:20.1.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-labels/python-labels.changes 2020-05-26 17:23:38.404532009 +0200
+++ /work/SRC/openSUSE:Factory/.python-labels.new.2275/python-labels.changes 2022-09-30 17:58:13.241303280 +0200
@@ -1,0 +2,7 @@
+Thu Sep 29 14:41:43 UTC 2022 - Yogalakshmi Arunachalam <yarunachalam(a)suse.com>
+
+- Update to Version 20.1.0
+ * This labels release adds support for paginated responses from the GitHub API on requesting a repository's labels.
+ This enables users to manage labels for projects that use more than 30 labels. rocket
+
+-------------------------------------------------------------------
Old:
----
labels-0.2.0.tar.gz
New:
----
labels-20.1.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-labels.spec ++++++
--- /var/tmp/diff_new_pack.A8PqX5/_old 2022-09-30 17:58:13.617304084 +0200
+++ /var/tmp/diff_new_pack.A8PqX5/_new 2022-09-30 17:58:13.625304102 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-labels
#
-# Copyright (c) 2020 SUSE LLC
+# Copyright (c) 2022 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%define skip_python2 1
Name: python-labels
-Version: 0.2.0
+Version: 20.1.0
Release: 0
Summary: CLI app for managing GitHub labels
License: MIT
@@ -34,7 +34,7 @@
Requires: python-pytoml
Requires: python-requests
Requires(post): update-alternatives
-Requires(postun): update-alternatives
+Requires(postun):update-alternatives
BuildArch: noarch
# SECTION test requirements
BuildRequires: %{python_module attrs}
++++++ labels-0.2.0.tar.gz -> labels-20.1.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labels-0.2.0/.github/CODE_OF_CONDUCT.md new/labels-20.1.0/.github/CODE_OF_CONDUCT.md
--- old/labels-0.2.0/.github/CODE_OF_CONDUCT.md 2019-11-15 18:35:58.000000000 +0100
+++ new/labels-20.1.0/.github/CODE_OF_CONDUCT.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,75 +0,0 @@
-# Contributor Covenant Code of Conduct
-
-## Our Pledge
-
-In the interest of fostering an open and welcoming environment, we as
-contributors and maintainers pledge to making participation in our project
-and our community a harassment-free experience for everyone, regardless of
-age, body size, disability, ethnicity, gender identity and expression, level
-of experience, nationality, personal appearance, race, religion, or sexual
-identity and orientation.
-
-## Our Standards
-
-Examples of behavior that contributes to creating a positive environment
-include:
-
-* Using welcoming and inclusive language
-* Being respectful of differing viewpoints and experiences
-* Gracefully accepting constructive criticism
-* Focusing on what is best for the community
-* Showing empathy towards other community members
-
-Examples of unacceptable behavior by participants include:
-
-* The use of sexualized language or imagery and unwelcome sexual attention or
- advances
-* Trolling, insulting/derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others' private information, such as a physical or electronic
- address, without explicit permission
-* Other conduct which could reasonably be considered inappropriate in a
- professional setting
-
-## Our Responsibilities
-
-Project maintainers are responsible for clarifying the standards of
-acceptable behavior and are expected to take appropriate and fair corrective
-action in response to any instances of unacceptable behavior.
-
-Project maintainers have the right and responsibility to remove, edit, or
-reject comments, commits, code, wiki edits, issues, and other contributions
-that are not aligned to this Code of Conduct, or to ban temporarily or
-permanently any contributor for other behaviors that they deem inappropriate,
-threatening, offensive, or harmful.
-
-## Scope
-
-This Code of Conduct applies both within project spaces and in public spaces
-when an individual is representing the project or its community. Examples of
-representing a project or community include using an official project e-mail
-address, posting via an official social media account, or acting as an
-appointed representative at an online or offline event. Representation of a
-project may be further defined and clarified by project maintainers.
-
-## Enforcement
-
-Instances of abusive, harassing, or otherwise unacceptable behavior may be
-reported by contacting the project team at raphael(a)hackebrot.de. The project
-team will review and investigate all complaints, and will respond in a way
-that it deems appropriate to the circumstances. The project team is obligated
-to maintain confidentiality with regard to the reporter of an incident.
-Further details of specific enforcement policies may be posted separately.
-
-Project maintainers who do not follow or enforce the Code of Conduct in good
-faith may face temporary or permanent repercussions as determined by other
-members of the project's leadership.
-
-## Attribution
-
-This Code of Conduct is adapted from the [Contributor Covenant][homepage],
-version 1.4, available at
-[http://contributor-covenant.org/version/1/4][version]
-
-[homepage]: http://contributor-covenant.org
-[version]: http://contributor-covenant.org/version/1/4/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labels-0.2.0/.github/COMMUNITY.md new/labels-20.1.0/.github/COMMUNITY.md
--- old/labels-0.2.0/.github/COMMUNITY.md 2019-11-15 18:35:58.000000000 +0100
+++ new/labels-20.1.0/.github/COMMUNITY.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,13 +0,0 @@
-# Community
-
-- [@Mariatta]
-- [@hackebrot]
-- [@mfonism]
-- [@michaeljoseph]
-- [@tprasadtp]
-
-[@Mariatta]: https://github.com/Mariatta
-[@hackebrot]: https://github.com/hackebrot
-[@mfonism]: https://github.com/mfonism
-[@michaeljoseph]: https://github.com/michaeljoseph
-[@tprasadtp]: https://github.com/tprasadtp
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labels-0.2.0/.github/CONTRIBUTING.md new/labels-20.1.0/.github/CONTRIBUTING.md
--- old/labels-0.2.0/.github/CONTRIBUTING.md 2019-11-15 18:35:58.000000000 +0100
+++ new/labels-20.1.0/.github/CONTRIBUTING.md 2020-05-04 16:55:36.000000000 +0200
@@ -21,5 +21,5 @@
[new issue]: https://github.com/hackebrot/labels/issues/new/choose
[new pull request]: https://github.com/hackebrot/labels/compare
[pull requests]: https://github.com/hackebrot/labels/pulls
-[code of conduct]: /.github/CODE_OF_CONDUCT.md
-[community]: /.github/COMMUNITY.md
+[code of conduct]: /CODE_OF_CONDUCT.md
+[community]: /COMMUNITY.md
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labels-0.2.0/.github/ISSUE_TEMPLATE/BUG_REPORT.md new/labels-20.1.0/.github/ISSUE_TEMPLATE/BUG_REPORT.md
--- old/labels-0.2.0/.github/ISSUE_TEMPLATE/BUG_REPORT.md 2019-11-15 18:35:58.000000000 +0100
+++ new/labels-20.1.0/.github/ISSUE_TEMPLATE/BUG_REPORT.md 2020-05-04 16:55:36.000000000 +0200
@@ -6,7 +6,7 @@
Please note that **labels** is released with a [Contributor Code of Conduct][code of conduct]. By participating in this project you agree to abide by its terms.
-[code of conduct]: /.github/CODE_OF_CONDUCT.md
+[code of conduct]: /CODE_OF_CONDUCT.md
## Bug Report
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labels-0.2.0/.github/ISSUE_TEMPLATE/OTHER.md new/labels-20.1.0/.github/ISSUE_TEMPLATE/OTHER.md
--- old/labels-0.2.0/.github/ISSUE_TEMPLATE/OTHER.md 2019-11-15 18:35:58.000000000 +0100
+++ new/labels-20.1.0/.github/ISSUE_TEMPLATE/OTHER.md 2020-05-04 16:55:36.000000000 +0200
@@ -6,7 +6,7 @@
Please note that **labels** is released with a [Contributor Code of Conduct][code of conduct]. By participating in this project you agree to abide by its terms.
-[code of conduct]: /.github/CODE_OF_CONDUCT.md
+[code of conduct]: /CODE_OF_CONDUCT.md
## Other
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labels-0.2.0/.github/PULL_REQUEST_TEMPLATE.md new/labels-20.1.0/.github/PULL_REQUEST_TEMPLATE.md
--- old/labels-0.2.0/.github/PULL_REQUEST_TEMPLATE.md 2019-11-15 18:35:58.000000000 +0100
+++ new/labels-20.1.0/.github/PULL_REQUEST_TEMPLATE.md 2020-05-04 16:55:36.000000000 +0200
@@ -2,5 +2,5 @@
Please note that **labels** is released with a [Contributor Code of Conduct][code of conduct]. By participating in this project you agree to abide by its terms.
-[code of conduct]: /.github/CODE_OF_CONDUCT.md
+[code of conduct]: /CODE_OF_CONDUCT.md
[contributing]: /.github/CONTRIBUTING.md
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labels-0.2.0/.github/labels.toml new/labels-20.1.0/.github/labels.toml
--- old/labels-0.2.0/.github/labels.toml 1970-01-01 01:00:00.000000000 +0100
+++ new/labels-20.1.0/.github/labels.toml 2020-05-04 16:55:36.000000000 +0200
@@ -0,0 +1,49 @@
+[bug]
+color = "ffeb95"
+name = "bug"
+description = "Bugs and problems with labels"
+
+["code quality"]
+color = "c792ea"
+name = "code quality"
+description = "Tasks related to linting, coding style, type checks"
+
+[dependencies]
+color = "c792ea"
+name = "dependencies"
+description = "Tasks related to managing dependencies"
+
+[discussion]
+color = "ffeb95"
+name = "discussion"
+description = "Issues for discussing ideas for features"
+
+["do not merge"]
+color = "ef5350"
+name = "do not merge"
+description = "Pull requests which must not be merged"
+
+[docs]
+color = "21c7a8"
+name = "docs"
+description = "Tasks to write and update documentation"
+
+[enhancement]
+color = "82aaff"
+name = "enhancement"
+description = "New feature or enhancement for labels"
+
+["good first issue"]
+color = "7fdbca"
+name = "good first issue"
+description = "Good tasks for newcomers to labels"
+
+[misc]
+color = "ecc48d"
+name = "misc"
+description = "Tasks that don't fit any of the other categories"
+
+[project]
+color = "d6deeb"
+name = "project"
+description = "Tasks related to managing this project"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labels-0.2.0/CODE_OF_CONDUCT.md new/labels-20.1.0/CODE_OF_CONDUCT.md
--- old/labels-0.2.0/CODE_OF_CONDUCT.md 1970-01-01 01:00:00.000000000 +0100
+++ new/labels-20.1.0/CODE_OF_CONDUCT.md 2020-05-04 16:55:36.000000000 +0200
@@ -0,0 +1,75 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project
+and our community a harassment-free experience for everyone, regardless of
+age, body size, disability, ethnicity, gender identity and expression, level
+of experience, nationality, personal appearance, race, religion, or sexual
+identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of
+acceptable behavior and are expected to take appropriate and fair corrective
+action in response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an
+appointed representative at an online or offline event. Representation of a
+project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at raphael(a)hackebrot.de. The project
+team will review and investigate all complaints, and will respond in a way
+that it deems appropriate to the circumstances. The project team is obligated
+to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 1.4, available at
+[http://contributor-covenant.org/version/1/4][version]
+
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/4/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labels-0.2.0/COMMUNITY.md new/labels-20.1.0/COMMUNITY.md
--- old/labels-0.2.0/COMMUNITY.md 1970-01-01 01:00:00.000000000 +0100
+++ new/labels-20.1.0/COMMUNITY.md 2020-05-04 16:55:36.000000000 +0200
@@ -0,0 +1,15 @@
+# Community
+
+- [@Mariatta]
+- [@hackebrot]
+- [@jean]
+- [@mfonism]
+- [@michaeljoseph]
+- [@tprasadtp]
+
+[@Mariatta]: https://github.com/Mariatta
+[@hackebrot]: https://github.com/hackebrot
+[@jean]: https://github.com/jean
+[@mfonism]: https://github.com/mfonism
+[@michaeljoseph]: https://github.com/michaeljoseph
+[@tprasadtp]: https://github.com/tprasadtp
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labels-0.2.0/MANIFEST.in new/labels-20.1.0/MANIFEST.in
--- old/labels-0.2.0/MANIFEST.in 2019-11-15 18:35:58.000000000 +0100
+++ new/labels-20.1.0/MANIFEST.in 2020-05-04 16:55:36.000000000 +0200
@@ -1,5 +1,7 @@
include LICENSE
include README.md
+include COMMUNITY.md
+include CODE_OF_CONDUCT.md
recursive-exclude * __pycache__
recursive-exclude * *.py[co]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labels-0.2.0/README.md new/labels-20.1.0/README.md
--- old/labels-0.2.0/README.md 2019-11-15 18:35:58.000000000 +0100
+++ new/labels-20.1.0/README.md 2020-05-04 16:55:36.000000000 +0200
@@ -10,6 +10,8 @@
pip install labels
```
+Versions follow [Calendar Versioning][calver] using a `YY.MINOR.MICRO` scheme. ����
+
## Authentication
The labels CLI connects to the GitHub API to modify labels for a GitHub
@@ -103,7 +105,6 @@
The section name (``[docs]`` in the example above) represents the name of the
label for that repository and is identical to the ``name`` field when running
``labels fetch``. Do not edit the section name of existing labels yourself!
-������
The fields ``color``, ``description`` and ``name`` are parameters that you
can edit with the **labels** CLI.
@@ -173,7 +174,8 @@
source software.
[PyPI]: https://pypi.org/project/labels/
-[code of conduct]: https://github.com/hackebrot/labels/blob/master/.github/CODE_OF_CONDUCT.md
+[calver]: https://calver.org
+[code of conduct]: https://github.com/hackebrot/labels/blob/master/CODE_OF_CONDUCT.md
[contributing]: https://github.com/hackebrot/labels/blob/master/.github/CONTRIBUTING.md
[create token]: https://blog.github.com/2013-05-16-personal-api-tokens/
[earth_repo]: https://github.com/hackebrot/earth
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labels-0.2.0/setup.py new/labels-20.1.0/setup.py
--- old/labels-0.2.0/setup.py 2019-11-15 18:35:58.000000000 +0100
+++ new/labels-20.1.0/setup.py 2020-05-04 16:55:36.000000000 +0200
@@ -9,7 +9,7 @@
setuptools.setup(
name="labels",
- version="0.2.0",
+ version="20.1.0",
author="Raphael Pierzina",
author_email="raphael(a)hackebrot.de",
maintainer="Raphael Pierzina",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labels-0.2.0/src/labels/__init__.py new/labels-20.1.0/src/labels/__init__.py
--- old/labels-0.2.0/src/labels/__init__.py 2019-11-15 18:35:58.000000000 +0100
+++ new/labels-20.1.0/src/labels/__init__.py 2020-05-04 16:55:36.000000000 +0200
@@ -1,4 +1,4 @@
-__version__ = "0.2.0"
+__version__ = "20.1.0"
__title__ = "labels"
__description__ = "CLI app for managing GitHub labels for Python 3.6 and newer. ����"
__url__ = "https://github.com/hackebrot/labels"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labels-0.2.0/src/labels/github.py new/labels-20.1.0/src/labels/github.py
--- old/labels-0.2.0/src/labels/github.py 2019-11-15 18:35:58.000000000 +0100
+++ new/labels-20.1.0/src/labels/github.py 2020-05-04 16:55:36.000000000 +0200
@@ -1,5 +1,5 @@
-import typing
import logging
+from typing import Any, Dict, List, Optional, Tuple
import attr
import requests
@@ -15,7 +15,7 @@
name: str
-def not_read_only(attr: attr.Attribute, value: typing.Any) -> bool:
+def not_read_only(attr: attr.Attribute, value: Any) -> bool:
"""Filter for attr that checks for a leading underscore."""
return not attr.name.startswith("_")
@@ -35,12 +35,12 @@
_url: str = ""
@property
- def params_dict(self) -> typing.Dict[str, typing.Any]:
+ def params_dict(self) -> Dict[str, Any]:
"""Return label parameters as a dict."""
return attr.asdict(self, recurse=True, filter=not_read_only)
@property
- def params_tuple(self) -> typing.Tuple[typing.Any, ...]:
+ def params_tuple(self) -> Tuple[Any, ...]:
"""Return label parameters as a tuple."""
return attr.astuple(self, recurse=True, filter=not_read_only)
@@ -56,7 +56,7 @@
self.session = requests.Session()
self.session.auth = auth
- def list_labels(self, repo: Repository) -> typing.List[Label]:
+ def list_labels(self, repo: Repository) -> List[Label]:
"""Return the list of Labels from the repository.
GitHub API docs:
@@ -65,9 +65,10 @@
logger = logging.getLogger("labels")
logger.debug(f"Requesting labels for {repo.owner}/{repo.name}")
+ headers = {"Accept": "application/vnd.github.symmetra-preview+json"}
+
response = self.session.get(
- f"{self.base_url}/repos/{repo.owner}/{repo.name}/labels",
- headers={"Accept": "application/vnd.github.symmetra-preview+json"},
+ f"{self.base_url}/repos/{repo.owner}/{repo.name}/labels", headers=headers
)
if response.status_code != 200:
@@ -77,7 +78,27 @@
f"{response.reason}"
)
- return [Label(**data) for data in response.json()]
+ repo_labels: List[Dict] = response.json()
+
+ next_page: Optional[Dict] = response.links.get("next", None)
+
+ while next_page is not None:
+
+ logger.debug(f"Requesting next page of labels")
+ response = self.session.get(next_page["url"], headers=headers)
+
+ if response.status_code != 200:
+ raise GitHubException(
+ f"Error retrieving next page of labels: "
+ f"{response.status_code} - "
+ f"{response.reason}"
+ )
+
+ repo_labels.extend(response.json())
+
+ next_page = response.links.get("next", None)
+
+ return [Label(**label) for label in repo_labels]
def get_label(self, repo: Repository, *, name: str) -> Label:
"""Return a single Label from the repository.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labels-0.2.0/tests/conftest.py new/labels-20.1.0/tests/conftest.py
--- old/labels-0.2.0/tests/conftest.py 2019-11-15 18:35:58.000000000 +0100
+++ new/labels-20.1.0/tests/conftest.py 2020-05-04 16:55:36.000000000 +0200
@@ -1,4 +1,4 @@
-import typing
+from typing import Any, Dict, Generator, List
import attr
import pytest
@@ -6,8 +6,8 @@
from labels.github import Label
-ResponseLabel = typing.Dict[str, typing.Any]
-ResponseLabels = typing.List[ResponseLabel]
+ResponseLabel = Dict[str, Any]
+ResponseLabels = List[ResponseLabel]
@pytest.fixture(name="username", scope="session")
@@ -34,6 +34,12 @@
return "turtle"
+(a)pytest.fixture(name="repo_id", scope="session")
+def fixture_repo_id() -> int:
+ """Return a repository ID."""
+ return 102909380
+
+
@attr.s(auto_attribs=True, frozen=True, kw_only=True)
class FakeProc:
"""Fake for a CompletedProcess instance."""
@@ -43,7 +49,7 @@
@pytest.fixture(name="mock_repo_info")
-def fixture_mock_repo_info(mocker: typing.Any, remote_url: str) -> typing.Any:
+def fixture_mock_repo_info(mocker: Any, remote_url: str) -> Any:
"""Patch the subprocess call to git remote get-url."""
return mocker.patch(
@@ -54,7 +60,7 @@
@pytest.fixture(name="mock_repo_info_error")
-def fixture_mock_repo_info_error(mocker: typing.Any) -> typing.Any:
+def fixture_mock_repo_info_error(mocker: Any) -> Any:
"""Patch the subprocess call to git remote get-url with an error."""
return mocker.patch(
@@ -65,7 +71,7 @@
@pytest.fixture(name="mock_repo_info_bad_url")
-def fixture_mock_repo_info_bad_url(mocker: typing.Any) -> typing.Any:
+def fixture_mock_repo_info_bad_url(mocker: Any) -> Any:
"""Patch the subprocess call to git remote get-url with a bad URL."""
return mocker.patch(
@@ -148,7 +154,7 @@
@pytest.fixture(name="mock_list_labels")
def fixture_mock_list_labels(
base_url: str, repo_owner: str, repo_name: str, response_list_labels: ResponseLabels
-) -> None:
+) -> Generator:
"""Mock requests for list labels."""
with responses.RequestsMock() as rsps:
rsps.add(
@@ -161,10 +167,55 @@
yield
+(a)pytest.fixture(name="mock_list_labels_paginated")
+def fixture_mock_list_labels_paginated(
+ base_url: str,
+ repo_owner: str,
+ repo_name: str,
+ repo_id: int,
+ response_get_infra: ResponseLabel,
+ response_get_docs: ResponseLabel,
+ response_get_bug: ResponseLabel,
+) -> Generator:
+ """Mock requests for list labels with pagination."""
+
+ with responses.RequestsMock() as rsps:
+
+ rsps.add(
+ responses.GET,
+ f"{base_url}/repos/{repo_owner}/{repo_name}/labels",
+ json=[response_get_bug, response_get_docs],
+ status=200,
+ content_type="application/json",
+ headers={
+ "Link": (
+ f'<{base_url}/repositories/{repo_id}/labels?page=2>; rel="next", '
+ f'<{base_url}/repositories/{repo_id}/labels?page=2>; rel="last"'
+ )
+ },
+ )
+
+ rsps.add(
+ responses.GET,
+ f"{base_url}/repositories/{repo_id}/labels?page=2",
+ json=[response_get_infra],
+ status=200,
+ content_type="application/json",
+ headers={
+ "Link": (
+ f'<{base_url}/repositories/{repo_id}/labels?page=1>; rel="prev", '
+ f'<{base_url}/repositories/{repo_id}/labels?page=1>; rel="first"'
+ )
+ },
+ )
+
+ yield
+
+
@pytest.fixture(name="mock_get_label")
def fixture_mock_get_label(
base_url: str, repo_owner: str, repo_name: str, response_get_bug: ResponseLabel
-) -> None:
+) -> Generator:
"""Mock requests for get label."""
with responses.RequestsMock() as rsps:
rsps.add(
@@ -180,7 +231,7 @@
@pytest.fixture(name="mock_edit_label")
def fixture_mock_edit_label(
base_url: str, repo_owner: str, repo_name: str, response_get_bug: ResponseLabel
-) -> None:
+) -> Generator:
"""Mock requests for edit label."""
with responses.RequestsMock() as rsps:
rsps.add(
@@ -208,7 +259,7 @@
repo_name: str,
label: Label,
response_get_bug: ResponseLabel,
-) -> None:
+) -> Generator:
"""Mock requests for create label."""
with responses.RequestsMock() as rsps:
rsps.add(
@@ -222,7 +273,9 @@
@pytest.fixture(name="mock_delete_label")
-def fixture_mock_delete_label(base_url: str, repo_owner: str, repo_name: str) -> None:
+def fixture_mock_delete_label(
+ base_url: str, repo_owner: str, repo_name: str
+) -> Generator:
"""Mock requests for delete label."""
with responses.RequestsMock() as rsps:
rsps.add(
@@ -236,7 +289,7 @@
@pytest.fixture(name="mock_sync")
def fixture_mock_sync(
base_url: str, repo_owner: str, repo_name: str, response_list_labels: ResponseLabels
-) -> None:
+) -> Generator:
with responses.RequestsMock() as rsps:
# Response mock for when sync requests the existing remote labels
rsps.add(
@@ -292,7 +345,7 @@
@pytest.fixture(name="labels")
-def fixture_labels() -> typing.List[Label]:
+def fixture_labels() -> List[Label]:
"""Return a list of Label instances."""
return [
Label(
@@ -334,7 +387,7 @@
@pytest.fixture(name="labels_file_dict")
-def fixture_labels_file_content() -> typing.Dict[str, typing.Any]:
+def fixture_labels_file_content() -> Dict[str, Any]:
"""Return a mapping from label names to dicts representing Labels."""
return {
"bug": {
@@ -386,7 +439,7 @@
@pytest.fixture(name="labels_file_write")
-def fixture_labels_file_write(tmpdir: typing.Any) -> str:
+def fixture_labels_file_write(tmpdir: Any) -> str:
"""Return a filepath to a temporary file."""
labels_file = tmpdir.join("labels.toml")
return str(labels_file)
@@ -399,6 +452,6 @@
@pytest.fixture(name="labels_file_sync")
-def fixture_labels_file_sync(tmpdir: typing.Any) -> str:
+def fixture_labels_file_sync(tmpdir: Any) -> str:
"""Return a filepath to an existing labels file for the sync test."""
return "tests/sync.toml"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labels-0.2.0/tests/test_github.py new/labels-20.1.0/tests/test_github.py
--- old/labels-0.2.0/tests/test_github.py 2019-11-15 18:35:58.000000000 +0100
+++ new/labels-20.1.0/tests/test_github.py 2020-05-04 16:55:36.000000000 +0200
@@ -45,6 +45,32 @@
assert [l.params_dict for l in labels] == expected_params
+(a)pytest.mark.usefixtures("mock_list_labels_paginated")
+def test_list_labels_pagination(client: Client, repo: Repository) -> None:
+ """Test that list_labels() supports pagination."""
+ labels = client.list_labels(repo)
+
+ expected_params = [
+ {
+ "name": "bug",
+ "description": "Bugs and problems with cookiecutter",
+ "color": "ea707a",
+ },
+ {
+ "name": "docs",
+ "description": "Tasks to write and update documentation",
+ "color": "2abf88",
+ },
+ {
+ "name": "infra",
+ "description": "Tasks related to Docker/CI etc.",
+ "color": "f9d03b",
+ },
+ ]
+
+ assert [l.params_dict for l in labels] == expected_params
+
+
@pytest.mark.usefixtures("mock_get_label")
def test_get_label(client: Client, repo: Repository) -> None:
"""Test that get_label() requests the specified label for the repo and
1
0
Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-librouteros for openSUSE:Factory checked in at 2022-09-30 17:57:53
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-librouteros (Old)
and /work/SRC/openSUSE:Factory/.python-librouteros.new.2275 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-librouteros"
Fri Sep 30 17:57:53 2022 rev:9 rq:1007072 version:3.2.1
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-librouteros/python-librouteros.changes 2022-03-24 22:57:54.968245650 +0100
+++ /work/SRC/openSUSE:Factory/.python-librouteros.new.2275/python-librouteros.changes 2022-09-30 17:58:11.105298714 +0200
@@ -1,0 +2,6 @@
+Thu Sep 29 14:49:07 UTC 2022 - Yogalakshmi Arunachalam <yarunachalam(a)suse.com>
+
+- Update to version 3.2.1
+ * Fix non ASCII API word encoding
+
+-------------------------------------------------------------------
Old:
----
librouteros-3.2.0.tar.gz
New:
----
librouteros-3.2.1.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-librouteros.spec ++++++
--- /var/tmp/diff_new_pack.ymV0wt/_old 2022-09-30 17:58:12.329301331 +0200
+++ /var/tmp/diff_new_pack.ymV0wt/_new 2022-09-30 17:58:12.333301339 +0200
@@ -20,7 +20,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%define skip_python2 1
Name: python-librouteros
-Version: 3.2.0
+Version: 3.2.1
Release: 0
Summary: Python implementation of MikroTik RouterOS API
License: GPL-2.0-or-later
++++++ librouteros-3.2.0.tar.gz -> librouteros-3.2.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/librouteros-3.2.0/.bumpversion.cfg new/librouteros-3.2.1/.bumpversion.cfg
--- old/librouteros-3.2.0/.bumpversion.cfg 2021-11-15 20:19:38.000000000 +0100
+++ new/librouteros-3.2.1/.bumpversion.cfg 2022-05-24 17:46:16.000000000 +0200
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 3.2.0
+current_version = 3.2.1
commit = True
tag = True
tag_name = {new_version}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/librouteros-3.2.0/.github/workflows/ci.yml new/librouteros-3.2.1/.github/workflows/ci.yml
--- old/librouteros-3.2.0/.github/workflows/ci.yml 2021-11-15 20:19:38.000000000 +0100
+++ new/librouteros-3.2.1/.github/workflows/ci.yml 2022-05-24 17:46:16.000000000 +0200
@@ -17,6 +17,7 @@
- 3.6
- 3.7
- 3.8
+ - 3.9
steps:
- name: Checkout
uses: actions/checkout@v2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/librouteros-3.2.0/CHANGELOG.rst new/librouteros-3.2.1/CHANGELOG.rst
--- old/librouteros-3.2.0/CHANGELOG.rst 2021-11-15 20:19:38.000000000 +0100
+++ new/librouteros-3.2.1/CHANGELOG.rst 2022-05-24 17:46:16.000000000 +0200
@@ -1,3 +1,8 @@
+3.2.1
+----------
+
+* Fix non ASCII API word encoding
+
3.2.0
----------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/librouteros-3.2.0/Pipfile new/librouteros-3.2.1/Pipfile
--- old/librouteros-3.2.0/Pipfile 2021-11-15 20:19:38.000000000 +0100
+++ new/librouteros-3.2.1/Pipfile 2022-05-24 17:46:16.000000000 +0200
@@ -14,3 +14,4 @@
bumpversion = "*"
twine = "*"
pytest-xdist = "*"
+toml = "*"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/librouteros-3.2.0/docker/3.6.dockerfile new/librouteros-3.2.1/docker/3.6.dockerfile
--- old/librouteros-3.2.0/docker/3.6.dockerfile 2021-11-15 20:19:38.000000000 +0100
+++ new/librouteros-3.2.1/docker/3.6.dockerfile 2022-05-24 17:46:16.000000000 +0200
@@ -1,13 +1,13 @@
-FROM python:3.6
+FROM python:3.6-slim
ENV DEBIAN_FRONTEND noninteractive
-RUN apt-get update
-RUN apt-get install -y --no-install-recommends \
+RUN apt-get update; \
+ apt-get install -y --no-install-recommends --no-install-suggests \
qemu-system-i386 \
- qemu-utils \
- wget
+ qemu-utils; \
+ pip install --no-cache-dir -U setuptools pip twine pipenv; \
+ apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
+ rm -rf /var/lib/apt/lists/*
COPY images/*.qcow2 /opt/
-RUN pip install -U setuptools pip
-RUN pip install twine pipenv
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/librouteros-3.2.0/docker/3.7.dockerfile new/librouteros-3.2.1/docker/3.7.dockerfile
--- old/librouteros-3.2.0/docker/3.7.dockerfile 2021-11-15 20:19:38.000000000 +0100
+++ new/librouteros-3.2.1/docker/3.7.dockerfile 2022-05-24 17:46:16.000000000 +0200
@@ -1,13 +1,13 @@
-FROM python:3.7
+FROM python:3.7-slim
ENV DEBIAN_FRONTEND noninteractive
-RUN apt-get update
-RUN apt-get install -y --no-install-recommends \
+RUN apt-get update; \
+ apt-get install -y --no-install-recommends --no-install-suggests \
qemu-system-i386 \
- qemu-utils \
- wget
+ qemu-utils; \
+ pip install --no-cache-dir -U setuptools pip twine pipenv; \
+ apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
+ rm -rf /var/lib/apt/lists/*
COPY images/*.qcow2 /opt/
-RUN pip install -U setuptools pip
-RUN pip install twine pipenv
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/librouteros-3.2.0/docker/3.8.dockerfile new/librouteros-3.2.1/docker/3.8.dockerfile
--- old/librouteros-3.2.0/docker/3.8.dockerfile 2021-11-15 20:19:38.000000000 +0100
+++ new/librouteros-3.2.1/docker/3.8.dockerfile 2022-05-24 17:46:16.000000000 +0200
@@ -1,13 +1,13 @@
-FROM python:3.8
+FROM python:3.8-slim
ENV DEBIAN_FRONTEND noninteractive
-RUN apt-get update
-RUN apt-get install -y --no-install-recommends \
+RUN apt-get update; \
+ apt-get install -y --no-install-recommends --no-install-suggests \
qemu-system-i386 \
- qemu-utils \
- wget
+ qemu-utils; \
+ pip install --no-cache-dir -U setuptools pip twine pipenv; \
+ apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
+ rm -rf /var/lib/apt/lists/*
COPY images/*.qcow2 /opt/
-RUN pip install -U setuptools pip
-RUN pip install twine pipenv
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/librouteros-3.2.0/docker/3.9.dockerfile new/librouteros-3.2.1/docker/3.9.dockerfile
--- old/librouteros-3.2.0/docker/3.9.dockerfile 1970-01-01 01:00:00.000000000 +0100
+++ new/librouteros-3.2.1/docker/3.9.dockerfile 2022-05-24 17:46:16.000000000 +0200
@@ -0,0 +1,13 @@
+FROM python:3.9-slim
+
+ENV DEBIAN_FRONTEND noninteractive
+
+RUN apt-get update; \
+ apt-get install -y --no-install-recommends --no-install-suggests \
+ qemu-system-i386 \
+ qemu-utils; \
+ pip install --no-cache-dir -U setuptools pip twine pipenv; \
+ apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
+ rm -rf /var/lib/apt/lists/*
+
+COPY images/*.qcow2 /opt/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/librouteros-3.2.0/docker/do.sh new/librouteros-3.2.1/docker/do.sh
--- old/librouteros-3.2.0/docker/do.sh 2021-11-15 20:19:38.000000000 +0100
+++ new/librouteros-3.2.1/docker/do.sh 2022-05-24 17:46:16.000000000 +0200
@@ -1,7 +1,6 @@
#!/bin/sh
-for ver in 3.6 3.7 3.8
+for ver in 3.6 3.7 3.8 3.9
do
- docker build -t lukaszkostka/librouteros:${ver} -f docker/${ver}.dockerfile .
- docker push lukaszkostka/librouteros:${ver}
+ docker buildx build --platform linux/amd64,linux/arm64 --push -t lukaszkostka/librouteros:${ver} -f docker/${ver}.dockerfile .
done
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/librouteros-3.2.0/docs/_static/custom.css new/librouteros-3.2.1/docs/_static/custom.css
--- old/librouteros-3.2.0/docs/_static/custom.css 1970-01-01 01:00:00.000000000 +0100
+++ new/librouteros-3.2.1/docs/_static/custom.css 2022-05-24 17:46:16.000000000 +0200
@@ -0,0 +1,6 @@
+div.body p, div.body dd, div.body li, div.body blockquote {
+ -moz-hyphens: none;
+ -ms-hyphens: none;
+ -webkit-hyphens: none;
+ hyphens: none;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/librouteros-3.2.0/docs/conf.py new/librouteros-3.2.1/docs/conf.py
--- old/librouteros-3.2.0/docs/conf.py 2021-11-15 20:19:38.000000000 +0100
+++ new/librouteros-3.2.1/docs/conf.py 2022-05-24 17:46:16.000000000 +0200
@@ -65,4 +65,7 @@
html_theme_options = {
'fixed_sidebar': True,
+ 'github_user': 'luqasz',
+ 'github_repo': 'librouteros',
+ 'page_width': '1000px',
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/librouteros-3.2.0/docs/connect.rst new/librouteros-3.2.1/docs/connect.rst
--- old/librouteros-3.2.0/docs/connect.rst 2021-11-15 20:19:38.000000000 +0100
+++ new/librouteros-3.2.1/docs/connect.rst 2022-05-24 17:46:16.000000000 +0200
@@ -11,7 +11,7 @@
username='admin',
password='abc',
host='some.address.com',
- )
+ )
Encrypted
---------
@@ -44,7 +44,10 @@
.. code-block:: python
from functools import partial
- ssl_wrapper=partial(ctx.wrap_socket, server_hostname='some.address.com')
+ ssl_wrapper=partial(
+ ctx.wrap_socket,
+ server_hostname='some.address.com',
+ )
Auth methods
------------
@@ -60,7 +63,12 @@
method = plain
# for pre 6.43 (with token)
method = token
- api = connect(username='admin', password='abc', host='some.address.com', login_method=method)
+ api = connect(
+ username='admin',
+ password='abc',
+ host='some.address.com',
+ login_method=method,
+ )
.. note::
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/librouteros-3.2.0/librouteros/protocol.py new/librouteros-3.2.1/librouteros/protocol.py
--- old/librouteros-3.2.0/librouteros/protocol.py 2021-11-15 20:19:38.000000000 +0100
+++ new/librouteros-3.2.1/librouteros/protocol.py 2022-05-24 17:46:16.000000000 +0200
@@ -72,7 +72,7 @@
"""
#pylint: disable=no-member
encoded_word = word.encode(encoding=self.encoding, errors='strict') # type: ignore
- return Encoder.encodeLength(len(word)) + encoded_word
+ return Encoder.encodeLength(len(encoded_word)) + encoded_word
@staticmethod
def encodeLength(length: int) -> bytes:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/librouteros-3.2.0/setup.py new/librouteros-3.2.1/setup.py
--- old/librouteros-3.2.0/setup.py 2021-11-15 20:19:38.000000000 +0100
+++ new/librouteros-3.2.1/setup.py 2022-05-24 17:46:16.000000000 +0200
@@ -28,7 +28,7 @@
setup_requires=setup_pkgs,
zip_safe=False,
name='librouteros',
- version='3.2.0',
+ version='3.2.1',
description='Python implementation of MikroTik RouterOS API',
long_description=read('README.rst'),
author='��ukasz Kostka',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/librouteros-3.2.0/tests/unit/conftest.py new/librouteros-3.2.1/tests/unit/conftest.py
--- old/librouteros-3.2.0/tests/unit/conftest.py 2021-11-15 20:19:38.000000000 +0100
+++ new/librouteros-3.2.1/tests/unit/conftest.py 2022-05-24 17:46:16.000000000 +0200
@@ -12,12 +12,6 @@
return b'\xff\xff\xff\xff\xff'
-(a)pytest.fixture(scope='function')
-def bad_length_int():
- """Length must be < 268435456"""
- return 268435456
-
-
@pytest.fixture(
scope='function',
params=(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/librouteros-3.2.0/tests/unit/test_protocol.py new/librouteros-3.2.1/tests/unit/test_protocol.py
--- old/librouteros-3.2.0/tests/unit/test_protocol.py 2021-11-15 20:19:38.000000000 +0100
+++ new/librouteros-3.2.1/tests/unit/test_protocol.py 2022-05-24 17:46:16.000000000 +0200
@@ -59,15 +59,11 @@
result = self.encoder.encodeLength(valid_word_length.integer)
assert result == valid_word_length.encoded
- def test_encodeLength_raises_if_lenghth_is_too_big(self, bad_length_int):
+ def test_encodeLength_raises_if_lenghth_is_too_big(self):
+ """Length must be < 268435456"""
+ invalid = 268435456
with pytest.raises(ProtocolError) as error:
- self.encoder.encodeLength(bad_length_int)
- assert str(bad_length_int) in str(error.value)
-
- @patch.object(Encoder, 'encodeLength', return_value=b'len_')
- def test_encodeWord(self, encodeLength_mock):
- assert self.encoder.encodeWord('word') == b'len_word'
- assert encodeLength_mock.call_count == 1
+ self.encoder.encodeLength(invalid)
def test_non_ASCII_word_encoding(self):
"""When encoding is ASCII, word may only contain ASCII characters."""
@@ -75,10 +71,18 @@
with pytest.raises(UnicodeEncodeError):
self.encoder.encodeWord(u'����')
- def test_utf_8_word_encoding(self):
- """Assert that utf-8 encoding works."""
+ @patch.object(Encoder, 'encodeLength', return_value=b'')
+ def test_utf_8_word_encoding(self, enc_len_mock):
+ """
+ Assert that len is taken from bytes, not utf8 word itself.
+
+ len('��') == 1
+ '��'.encode(encoding='UTF8') == 2
+ """
self.encoder.encoding = 'utf-8'
- assert self.encoder.encodeWord(u'����').endswith(b'\xc5\x82\xc4\x85')
+ word = '��'
+ self.encoder.encodeWord(word)
+ enc_len_mock.assert_called_once_with(len(word.encode(self.encoder.encoding)))
@patch.object(Encoder, 'encodeWord', return_value=b'')
def test_encodeSentence(self, encodeWord_mock):
1
0
Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-logilab-common for openSUSE:Factory checked in at 2022-09-30 17:57:51
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-logilab-common (Old)
and /work/SRC/openSUSE:Factory/.python-logilab-common.new.2275 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-logilab-common"
Fri Sep 30 17:57:51 2022 rev:10 rq:1007070 version:1.9.7
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-logilab-common/python-logilab-common.changes 2022-02-15 00:10:56.471365590 +0100
+++ /work/SRC/openSUSE:Factory/.python-logilab-common.new.2275/python-logilab-common.changes 2022-09-30 17:58:09.081294388 +0200
@@ -1,0 +2,6 @@
+Thu Sep 29 14:53:49 UTC 2022 - Yogalakshmi Arunachalam <yarunachalam(a)suse.com>
+
+- Update to 1.9.7
+ No log
+
+-------------------------------------------------------------------
Old:
----
logilab-common-1.8.3.tar.gz
New:
----
logilab-common-1.9.7.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-logilab-common.spec ++++++
--- /var/tmp/diff_new_pack.HSL4HI/_old 2022-09-30 17:58:10.137296645 +0200
+++ /var/tmp/diff_new_pack.HSL4HI/_new 2022-09-30 17:58:10.145296663 +0200
@@ -18,7 +18,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-logilab-common
-Version: 1.8.3
+Version: 1.9.7
Release: 0
Summary: Python lowlevel functionality shared by logilab projects
License: LGPL-2.1-or-later
++++++ logilab-common-1.8.3.tar.gz -> logilab-common-1.9.7.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-1.8.3/PKG-INFO new/logilab-common-1.9.7/PKG-INFO
--- old/logilab-common-1.8.3/PKG-INFO 2022-01-04 16:19:42.000000000 +0100
+++ new/logilab-common-1.9.7/PKG-INFO 2022-06-07 16:22:58.951329500 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: logilab-common
-Version: 1.8.3
+Version: 1.9.7
Summary: collection of low-level Python packages and modules used by Logilab projects
Home-page: http://www.logilab.org/project/logilab-common
Author: Logilab
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-1.8.3/__pkginfo__.py new/logilab-common-1.9.7/__pkginfo__.py
--- old/logilab-common-1.8.3/__pkginfo__.py 2022-01-04 16:17:46.000000000 +0100
+++ new/logilab-common-1.9.7/__pkginfo__.py 2022-06-07 16:22:37.000000000 +0200
@@ -21,6 +21,7 @@
__docformat__ = "restructuredtext en"
import os
+import sys
from os.path import join
distname = "logilab-common"
@@ -28,7 +29,7 @@
subpackage_of = "logilab"
subpackage_master = True
-numversion = (1, 8, 3)
+numversion = (1, 9, 7)
version = ".".join([str(num) for num in numversion])
license = "LGPL" # 2.1 or later
@@ -42,18 +43,21 @@
scripts = [join("bin", "logilab-pytest")]
include_dirs = [join("test", "data")]
-install_requires = [
- "setuptools",
- "mypy-extensions",
- "typing_extensions",
-]
+__depends__ = {
+ "setuptools": None,
+ "mypy-extensions": None,
+ "typing_extensions": None,
+}
tests_require = [
"pytz",
"egenix-mx-base",
]
if os.name == "nt":
- install_requires.append("colorama")
+ __depends__["colorama"] = None
+
+if sys.version_info < (3, 8):
+ __depends__["importlib_metadata"] = None
classifiers = [
"Topic :: Utilities",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-1.8.3/logilab/common/decorators.py new/logilab-common-1.9.7/logilab/common/decorators.py
--- old/logilab-common-1.8.3/logilab/common/decorators.py 2022-01-04 15:03:47.000000000 +0100
+++ new/logilab-common-1.9.7/logilab/common/decorators.py 2022-06-07 16:22:38.000000000 +0200
@@ -25,7 +25,7 @@
from time import process_time, time
from inspect import isgeneratorfunction
-from typing import Any, Optional, Callable, Union
+from typing import Any, Optional, Callable, overload, TypeVar
from inspect import getfullargspec
@@ -121,9 +121,22 @@
return _cache[key]
+_T = TypeVar("_T", bound=Callable)
+
+
+@overload
def cached(
- callableobj: Optional[Callable] = None, keyarg: Optional[int] = None, **kwargs: Any
-) -> Union[Callable, cached_decorator]:
+ callableobj: None = None, keyarg: Optional[int] = None, **kwargs: Any
+) -> Callable[[_T], _T]:
+ ...
+
+
+@overload
+def cached(callableobj: _T = None, keyarg: Optional[int] = None, **kwargs: Any) -> _T:
+ ...
+
+
+def cached(callableobj=None, keyarg=None, **kwargs):
"""Simple decorator to cache result of method call."""
kwargs["keyarg"] = keyarg
decorator = cached_decorator(**kwargs)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-1.8.3/logilab/common/deprecation.py new/logilab-common-1.9.7/logilab/common/deprecation.py
--- old/logilab-common-1.8.3/logilab/common/deprecation.py 2020-12-17 15:03:52.000000000 +0100
+++ new/logilab-common-1.9.7/logilab/common/deprecation.py 2022-06-07 16:22:38.000000000 +0200
@@ -21,14 +21,20 @@
import os
import sys
+import inspect
from enum import Enum
from warnings import warn
from functools import WRAPPER_ASSIGNMENTS, WRAPPER_UPDATES
from typing import Any, Callable, Dict, Optional, Type
from typing_extensions import Protocol
+if sys.version_info >= (3, 8):
+ from importlib import metadata as importlib_metadata
+else:
+ import importlib_metadata
-def get_real__name__(some_callable: Callable) -> str:
+
+def _unstack_all_deprecation_decorators(function):
"""
This is another super edge magic case which is needed because we uses
lazy_wraps because of logilab.common.modutils.LazyObject and because
@@ -38,12 +44,18 @@
Therefor, to get the real callable name when several lazy_wrapped
decorator are used we need to travers the __wrapped__ attributes chain.
"""
+ while hasattr(function, "__wrapped__"):
+ function = function.__wrapped__
+
+ return function
- targeted_callable = some_callable
- while hasattr(targeted_callable, "__wrapped__"):
- targeted_callable = targeted_callable.__wrapped__ # type: ignore
- return targeted_callable.__name__
+def get_real__name__(some_callable: Callable) -> str:
+ return _unstack_all_deprecation_decorators(some_callable).__name__
+
+
+def get_real__module__(some_callable: Callable) -> str:
+ return _unstack_all_deprecation_decorators(some_callable).__module__
def lazy_wraps(wrapped: Callable) -> Callable:
@@ -145,6 +157,42 @@
return file_name
+_cached_path_to_package: Optional[Dict[str, Optional[str]]] = None
+
+
+def _get_package_name(python_object) -> Optional[str]:
+ # only do this work if we are in a pytest session
+ if "COLLECT_DEPRECATION_WARNINGS_PACKAGE_NAME" not in os.environ:
+ return None
+
+ global _cached_path_to_package
+
+ if _cached_path_to_package is None:
+ _cached_path_to_package = {}
+ # mypy fails to understand the result of .discover(): Cannot
+ # instantiate abstract class 'Distribution' with abstract attributes
+ # 'locate_file' and 'read_text'
+ for distribution in importlib_metadata.Distribution().discover(): # type: ignore
+ # sometime distribution has a "name" attribute, sometime not
+ if distribution.files and hasattr(distribution, "name"):
+ for file in distribution.files:
+ _cached_path_to_package[str(distribution.locate_file(file))] = distribution.name
+ continue
+
+ if distribution.files and "name" in distribution.metadata:
+ for file in distribution.files:
+ _cached_path_to_package[
+ str(distribution.locate_file(file))
+ ] = distribution.metadata["name"]
+
+ try:
+ return _cached_path_to_package.get(
+ inspect.getfile(_unstack_all_deprecation_decorators(python_object))
+ )
+ except TypeError:
+ return None
+
+
def send_warning(
reason: str,
deprecation_class: Type[DeprecationWarning],
@@ -189,16 +237,26 @@
Mostly used with isinstance
"""
- def __init__(self, reason: str):
+ def __init__(self, reason: str, package: str = None, version: str = None):
self.reason: str = reason
+ self.package = package
+ self.version = version
def __str__(self) -> str:
return self.reason
class TargetRenamedDeprecationWarning(StructuredDeprecationWarning):
- def __init__(self, reason: str, kind: DeprecationWarningKind, old_name: str, new_name: str):
- super().__init__(reason)
+ def __init__(
+ self,
+ reason: str,
+ kind: DeprecationWarningKind,
+ old_name: str,
+ new_name: str,
+ package: str = None,
+ version: str = None,
+ ):
+ super().__init__(reason, package=package, version=version)
self.operation = DeprecationWarningOperation.RENAMED
self.kind: DeprecationWarningKind = kind # callable, class, module, argument, attribute
self.old_name: str = old_name
@@ -206,15 +264,24 @@
class TargetDeprecatedDeprecationWarning(StructuredDeprecationWarning):
- def __init__(self, reason: str, kind: DeprecationWarningKind):
- super().__init__(reason)
+ def __init__(
+ self, reason: str, kind: DeprecationWarningKind, package: str = None, version: str = None
+ ):
+ super().__init__(reason, package=package, version=version)
self.operation = DeprecationWarningOperation.DEPRECATED
self.kind: DeprecationWarningKind = kind # callable, class, module, argument, attribute
class TargetRemovedDeprecationWarning(StructuredDeprecationWarning):
- def __init__(self, reason: str, kind: DeprecationWarningKind, name: str):
- super().__init__(reason)
+ def __init__(
+ self,
+ reason: str,
+ kind: DeprecationWarningKind,
+ name: str,
+ package: str = None,
+ version: str = None,
+ ):
+ super().__init__(reason, package=package, version=version)
self.operation = DeprecationWarningOperation.REMOVED
self.kind: DeprecationWarningKind = kind # callable, class, module, argument, attribute
self.name: str = name
@@ -229,8 +296,10 @@
new_name: str,
old_module: str,
new_module: str,
+ package: str = None,
+ version: str = None,
):
- super().__init__(reason)
+ super().__init__(reason, package=package, version=version)
self.operation = DeprecationWarningOperation.MOVED
self.kind: DeprecationWarningKind = kind # callable, class, module, argument, attribute
self.old_name: str = old_name
@@ -266,10 +335,12 @@
"kind": DeprecationWarningKind.CALLABLE,
"old_name": old_name,
"new_name": get_real__name__(new_function),
+ "version": version,
+ "package": _get_package_name(new_function),
},
stacklevel=3,
version=version,
- module_name=new_function.__module__,
+ module_name=get_real__module__(new_function),
)
return new_function(*args, **kwargs)
@@ -305,10 +376,12 @@
deprecation_class_kwargs={
"kind": DeprecationWarningKind.ARGUMENT,
"name": old_argument_name,
+ "version": version,
+ "package": _get_package_name(func),
},
stacklevel=3,
version=version,
- module_name=func.__module__,
+ module_name=get_real__module__(func),
)
del kwargs[old_argument_name]
@@ -337,11 +410,15 @@
send_warning(
message,
- TargetDeprecatedDeprecationWarning,
- {"kind": DeprecationWarningKind.CALLABLE},
- version,
- stacklevel + 1,
- module_name=func.__module__,
+ deprecation_class=TargetDeprecatedDeprecationWarning,
+ deprecation_class_kwargs={
+ "kind": DeprecationWarningKind.CALLABLE,
+ "version": version,
+ "package": _get_package_name(func),
+ },
+ version=version,
+ stacklevel=stacklevel + 1,
+ module_name=get_real__module__(func),
)
return func(*args, **kwargs)
@@ -368,7 +445,7 @@
def __call__(cls, *args, **kwargs):
message = getattr(cls, "__deprecation_warning__", "%(cls)s is deprecated") % {
- "cls": cls.__name__
+ "cls": get_real__name__(cls)
}
send_warning(
message,
@@ -378,7 +455,11 @@
deprecation_class_kwargs=getattr(
cls,
"__deprecation_warning_class_kwargs__",
- {"kind": DeprecationWarningKind.CLASS},
+ {
+ "kind": DeprecationWarningKind.CLASS,
+ "package": _get_package_name(cls),
+ "version": getattr(cls, "__deprecation_warning_version__", None),
+ },
),
module_name=getattr(
cls, "__deprecation_warning_module_name__", _get_module_name(1)
@@ -419,8 +500,8 @@
def _class_wrap(klass: type) -> type:
reason = (
- f"{klass.__name__}.{old_name} has been renamed and is deprecated, use "
- f"{klass.__name__}.{new_name} instead"
+ f"{get_real__name__(klass)}.{old_name} has been renamed and is deprecated, use "
+ f"{get_real__name__(klass)}.{new_name} instead"
)
def _get_old(self) -> Any:
@@ -431,10 +512,12 @@
"kind": DeprecationWarningKind.ATTRIBUTE,
"old_name": old_name,
"new_name": new_name,
+ "version": version,
+ "package": _get_package_name(klass),
},
stacklevel=3,
version=version,
- module_name=klass.__module__,
+ module_name=get_real__module__(klass),
)
return getattr(self, new_name)
@@ -446,10 +529,12 @@
"kind": DeprecationWarningKind.ATTRIBUTE,
"old_name": old_name,
"new_name": new_name,
+ "version": version,
+ "package": _get_package_name(klass),
},
stacklevel=3,
version=version,
- module_name=klass.__module__,
+ module_name=get_real__module__(klass),
)
setattr(self, new_name, value)
@@ -461,10 +546,12 @@
"kind": DeprecationWarningKind.ATTRIBUTE,
"old_name": old_name,
"new_name": new_name,
+ "version": version,
+ "package": _get_package_name(klass),
},
stacklevel=3,
version=version,
- module_name=klass.__module__,
+ module_name=get_real__module__(klass),
)
delattr(self, new_name)
@@ -508,10 +595,12 @@
"kind": DeprecationWarningKind.ARGUMENT,
"old_name": old_name,
"new_name": new_name,
+ "version": version,
+ "package": _get_package_name(func),
},
stacklevel=3,
version=version,
- module_name=func.__module__,
+ module_name=get_real__module__(func),
)
kwargs[new_name] = kwargs[old_name]
del kwargs[old_name]
@@ -556,6 +645,8 @@
def callnew(*args, **kwargs):
from logilab.common.modutils import load_module_from_name
+ m = load_module_from_name(module_name)
+
send_warning(
message,
deprecation_class=TargetMovedDeprecationWarning,
@@ -565,13 +656,14 @@
"new_name": new_name,
"old_module": old_module,
"new_module": module_name,
+ "version": version,
+ "package": _get_package_name(getattr(m, object_name)),
},
version=version,
stacklevel=stacklevel + 1,
module_name=old_module,
)
- m = load_module_from_name(module_name)
return getattr(m, object_name)(*args, **kwargs)
return callnew
@@ -602,7 +694,7 @@
"""
class_dict: Dict[str, Any] = {}
if message is None:
- message = "%s is deprecated, use %s instead" % (old_name, new_class.__name__)
+ message = "%s is deprecated, use %s instead" % (old_name, get_real__name__(new_class))
class_dict["__deprecation_warning__"] = message
class_dict["__deprecation_warning_class__"] = deprecated_warning_class
@@ -610,7 +702,9 @@
class_dict["__deprecation_warning_class_kwargs__"] = {
"kind": DeprecationWarningKind.CLASS,
"old_name": old_name,
- "new_name": new_class.__name__,
+ "new_name": get_real__name__(new_class),
+ "version": version,
+ "package": _get_package_name(new_class),
}
else:
class_dict["__deprecation_warning_class_kwargs__"] = deprecated_warning_kwargs
@@ -631,7 +725,7 @@
def __init__(self, *args, **kwargs):
msg = class_dict.get(
"__deprecation_warning__",
- f"{old_name} is deprecated, use {new_class.__name__} instead",
+ f"{old_name} is deprecated, use {get_real__name__(new_class)} instead",
)
send_warning(
msg,
@@ -639,7 +733,9 @@
deprecation_class_kwargs={
"kind": DeprecationWarningKind.CLASS,
"old_name": old_name,
- "new_name": new_class.__name__,
+ "new_name": get_real__name__(new_class),
+ "version": version,
+ "package": _get_package_name(new_class),
},
stacklevel=class_dict.get("__deprecation_warning_stacklevel__", 3),
version=class_dict.get("__deprecation_warning_version__", None),
@@ -659,7 +755,7 @@
another module
"""
if old_name is None:
- old_name = new_class.__name__
+ old_name = get_real__name__(new_class)
old_module = _get_module_name(1)
@@ -667,8 +763,8 @@
message = "class %s.%s is now available as %s.%s" % (
old_module,
old_name,
- new_class.__module__,
- new_class.__name__,
+ get_real__module__(new_class),
+ get_real__name__(new_class),
)
module_name = _get_module_name(1)
@@ -677,13 +773,16 @@
old_name,
new_class,
message=message,
+ version=version,
module_name=module_name,
deprecated_warning_class=TargetMovedDeprecationWarning,
deprecated_warning_kwargs={
"kind": DeprecationWarningKind.CLASS,
"old_module": old_module,
- "new_module": new_class.__module__,
+ "new_module": get_real__module__(new_class),
"old_name": old_name,
- "new_name": new_class.__name__,
+ "new_name": get_real__name__(new_class),
+ "version": version,
+ "package": _get_package_name(new_class),
},
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-1.8.3/logilab/common/textutils.py new/logilab-common-1.9.7/logilab/common/textutils.py
--- old/logilab-common-1.8.3/logilab/common/textutils.py 2020-12-17 15:03:52.000000000 +0100
+++ new/logilab-common-1.9.7/logilab/common/textutils.py 2022-06-07 16:22:38.000000000 +0200
@@ -120,7 +120,7 @@
replacement = MANUAL_UNICODE_MAP[letter]
except KeyError:
replacement = _uninormalize("NFKD", letter)[0]
- if ord(replacement) >= 2 ** 7:
+ if ord(replacement) >= 2**7:
if substitute is None:
raise ValueError("can't deal with non-ascii based characters")
replacement = substitute
@@ -347,9 +347,9 @@
BYTE_UNITS = {
"b": 1,
"kb": 1024,
- "mb": 1024 ** 2,
- "gb": 1024 ** 3,
- "tb": 1024 ** 4,
+ "mb": 1024**2,
+ "gb": 1024**3,
+ "tb": 1024**4,
}
TIME_UNITS = {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-1.8.3/logilab_common.egg-info/PKG-INFO new/logilab-common-1.9.7/logilab_common.egg-info/PKG-INFO
--- old/logilab-common-1.8.3/logilab_common.egg-info/PKG-INFO 2022-01-04 16:19:41.000000000 +0100
+++ new/logilab-common-1.9.7/logilab_common.egg-info/PKG-INFO 2022-06-07 16:22:57.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: logilab-common
-Version: 1.8.3
+Version: 1.9.7
Summary: collection of low-level Python packages and modules used by Logilab projects
Home-page: http://www.logilab.org/project/logilab-common
Author: Logilab
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-1.8.3/setup.py new/logilab-common-1.9.7/setup.py
--- old/logilab-common-1.8.3/setup.py 2022-01-04 15:03:47.000000000 +0100
+++ new/logilab-common-1.9.7/setup.py 2022-06-07 16:22:38.000000000 +0200
@@ -34,6 +34,11 @@
with open(path.join(here, "README.rst"), encoding="utf-8") as f:
long_description = f.read()
+requires = {}
+for entry in ("__depends__",): # "__recommends__"):
+ requires.update(pkginfo.get(entry, {}))
+install_requires = ["{0} {1}".format(d, v and v or "").strip() for d, v in requires.items()]
+
setup(
name=pkginfo["distname"],
version=pkginfo["version"],
@@ -50,7 +55,7 @@
include_package_data=True,
namespace_packages=[pkginfo["subpackage_of"]],
python_requires=">=3.6",
- install_requires=pkginfo["install_requires"],
+ install_requires=install_requires,
tests_require=pkginfo["tests_require"],
scripts=pkginfo["scripts"],
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-1.8.3/test/data/module2.py new/logilab-common-1.9.7/test/data/module2.py
--- old/logilab-common-1.8.3/test/data/module2.py 2022-01-04 15:03:47.000000000 +0100
+++ new/logilab-common-1.9.7/test/data/module2.py 2022-06-07 16:22:38.000000000 +0200
@@ -78,7 +78,7 @@
c = b // 2
c = b - 2
c = b % 2
-c = b ** 2
+c = b**2
c = b << 2
c = b >> 2
c = ~b
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-1.8.3/test/test_date.py new/logilab-common-1.9.7/test/test_date.py
--- old/logilab-common-1.8.3/test/test_date.py 2020-12-17 15:03:52.000000000 +0100
+++ new/logilab-common-1.9.7/test/test_date.py 2022-06-07 16:22:38.000000000 +0200
@@ -78,12 +78,13 @@
def test_get_national_holidays(self):
holidays = get_national_holidays
- yield self.assertEqual, holidays(self.datecls(2008, 4, 29), self.datecls(2008, 5, 2)), [
- self.datecls(2008, 5, 1)
- ]
- yield self.assertEqual, holidays(self.datecls(2008, 5, 7), self.datecls(2008, 5, 8)), []
+ self.assertEqual(
+ holidays(self.datecls(2008, 4, 29), self.datecls(2008, 5, 2)),
+ [self.datecls(2008, 5, 1)],
+ )
+ self.assertEqual(holidays(self.datecls(2008, 5, 7), self.datecls(2008, 5, 8)), [])
x = self.datetimecls(2008, 5, 7, 12, 12, 12)
- yield self.assertEqual, holidays(x, x + self.timedeltacls(days=1)), []
+ self.assertEqual(holidays(x, x + self.timedeltacls(days=1)), [])
def test_open_days_now_and_before(self):
nb = nb_open_days
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-1.8.3/test/test_deprecation.py new/logilab-common-1.9.7/test/test_deprecation.py
--- old/logilab-common-1.8.3/test/test_deprecation.py 2020-12-17 15:03:52.000000000 +0100
+++ new/logilab-common-1.9.7/test/test_deprecation.py 2022-06-07 16:22:38.000000000 +0200
@@ -57,17 +57,35 @@
pass
AnyClass()
- self.assertEqual(self.messages, ["[test_deprecation] AnyClass is deprecated"])
+
+ class AnyClass(object, metaclass=deprecation.class_deprecated):
+ __deprecation_warning_version__ = "1.2.3"
+
+ AnyClass()
+
+ self.assertEqual(
+ self.messages,
+ [
+ "[test_deprecation] AnyClass is deprecated",
+ "[test_deprecation 1.2.3] AnyClass is deprecated",
+ ],
+ )
def test_class_renamed(self):
class AnyClass(object):
pass
OldClass = deprecation.class_renamed("OldClass", AnyClass)
-
OldClass()
+ OldClass = deprecation.class_renamed("OldClass", AnyClass, version="1.2.3")
+ OldClass()
+
self.assertEqual(
- self.messages, ["[test_deprecation] OldClass is deprecated, use AnyClass instead"]
+ self.messages,
+ [
+ "[test_deprecation] OldClass is deprecated, use AnyClass instead",
+ "[test_deprecation 1.2.3] OldClass is deprecated, use AnyClass instead",
+ ],
)
def test_class_renamed_conflict_metaclass(self):
@@ -77,9 +95,10 @@
class AnyClass(metaclass=SomeMetaClass):
pass
- # make sure the "metaclass conflict: the metaclass of a derived class # must be a
+ # make sure the "metaclass conflict: the metaclass of a derived class must be a
# (non-strict) subclass of the metaclasses of all its bases" exception won't be raised
deprecation.class_renamed("OldClass", AnyClass)
+ deprecation.class_renamed("OldClass", AnyClass, version="1.2.3")
def test_class_moved(self):
class AnyClass(object):
@@ -87,24 +106,33 @@
OldClass = deprecation.class_moved(new_class=AnyClass, old_name="OldName")
OldClass()
+ OldClass = deprecation.class_moved(new_class=AnyClass, old_name="OldName", version="1.2.3")
+ OldClass()
self.assertEqual(
self.messages,
[
"[test_deprecation] class test_deprecation.OldName is now available as "
- "test_deprecation.AnyClass"
+ "test_deprecation.AnyClass",
+ "[test_deprecation 1.2.3] class test_deprecation.OldName is now available as "
+ "test_deprecation.AnyClass",
],
)
self.messages = []
- AnyClass = deprecation.class_moved(new_class=AnyClass)
+ AnyClass2 = deprecation.class_moved(new_class=AnyClass)
+ AnyClass2()
+
+ AnyClass3 = deprecation.class_moved(new_class=AnyClass, version="1.2.3")
+ AnyClass3()
- AnyClass()
self.assertEqual(
self.messages,
[
"[test_deprecation] class test_deprecation.AnyClass is now available as "
- "test_deprecation.AnyClass"
+ "test_deprecation.AnyClass",
+ "[test_deprecation 1.2.3] class test_deprecation.AnyClass is now available as "
+ "test_deprecation.AnyClass",
],
)
@@ -113,11 +141,17 @@
any_func()
any_func = deprecation.callable_deprecated("message")(self.mk_func())
any_func()
+ any_func = deprecation.callable_deprecated(version="1.2.3")(self.mk_func())
+ any_func()
+ any_func = deprecation.callable_deprecated("message", version="1.2.3")(self.mk_func())
+ any_func()
self.assertEqual(
self.messages,
[
'[test_deprecation] The function "any_func" is deprecated',
"[test_deprecation] message",
+ '[test_deprecation 1.2.3] The function "any_func" is deprecated',
+ "[test_deprecation 1.2.3] message",
],
)
@@ -133,11 +167,25 @@
pass
any_func()
+
+ @deprecation.callable_deprecated(version="1.2.3")
+ def any_func():
+ pass
+
+ any_func()
+
+ @deprecation.callable_deprecated("message", version="1.2.3")
+ def any_func():
+ pass
+
+ any_func()
self.assertEqual(
self.messages,
[
'[test_deprecation] The function "any_func" is deprecated',
"[test_deprecation] message",
+ '[test_deprecation 1.2.3] The function "any_func" is deprecated',
+ "[test_deprecation 1.2.3] message",
],
)
@@ -206,6 +254,32 @@
self.assertFalse(hasattr(some_class, "new"))
self.assertFalse(hasattr(some_class, "old"))
+ def test_attribute_renamed_version(self):
+ @deprecation.attribute_renamed(old_name="old", new_name="new", version="1.2.3")
+ class SomeClass:
+ def __init__(self):
+ self.new = 42
+
+ some_class = SomeClass()
+ self.assertEqual(some_class.old, some_class.new)
+ self.assertEqual(
+ self.messages,
+ [
+ "[test_deprecation 1.2.3] SomeClass.old has been renamed and is deprecated, "
+ "use SomeClass.new instead"
+ ],
+ )
+
+ some_class.old = 43
+ self.assertEqual(some_class.old, 43)
+ self.assertEqual(some_class.old, some_class.new)
+
+ self.assertTrue(hasattr(some_class, "new"))
+ self.assertTrue(hasattr(some_class, "old"))
+ del some_class.old
+ self.assertFalse(hasattr(some_class, "new"))
+ self.assertFalse(hasattr(some_class, "old"))
+
def test_argument_renamed(self):
@deprecation.argument_renamed(old_name="old", new_name="new")
def some_function(new):
@@ -224,6 +298,24 @@
with self.assertRaises(ValueError):
some_function(new=42, old=42)
+ def test_argument_renamed_version(self):
+ @deprecation.argument_renamed(old_name="old", new_name="new", version="1.2.3")
+ def some_function(new):
+ return new
+
+ self.assertEqual(some_function(new=42), 42)
+ self.assertEqual(some_function(old=42), 42)
+ self.assertEqual(
+ self.messages,
+ [
+ "[test_deprecation 1.2.3] argument old of callable some_function has been renamed "
+ "and is deprecated, use keyword argument new instead"
+ ],
+ )
+
+ with self.assertRaises(ValueError):
+ some_function(new=42, old=42)
+
def test_argument_removed(self):
@deprecation.argument_removed("old")
def some_function(new):
@@ -239,6 +331,21 @@
],
)
+ def test_argument_removed_version(self):
+ @deprecation.argument_removed("old", version="1.2.3")
+ def some_function(new):
+ return new
+
+ self.assertEqual(some_function(new=42), 42)
+ self.assertEqual(some_function(new=10, old=20), 10)
+ self.assertEqual(
+ self.messages,
+ [
+ "[test_deprecation 1.2.3] argument old of callable some_function has been removed "
+ "and is deprecated"
+ ],
+ )
+
def test_callable_renamed(self):
def any_func():
pass
@@ -254,6 +361,21 @@
],
)
+ def test_callable_renamed_version(self):
+ def any_func():
+ pass
+
+ old_func = deprecation.callable_renamed("old_func", any_func, version="1.2.3")
+ old_func()
+
+ self.assertEqual(
+ self.messages,
+ [
+ "[test_deprecation 1.2.3] old_func has been renamed and is deprecated, "
+ "uses any_func instead"
+ ],
+ )
+
def test_callable_moved(self):
module = "data.deprecation"
moving_target = deprecation.callable_moved(module, "moving_target")
@@ -266,6 +388,18 @@
],
)
+ def test_callable_moved_version(self):
+ module = "data.deprecation"
+ moving_target = deprecation.callable_moved(module, "moving_target", version="1.2.3")
+ moving_target()
+ self.assertEqual(
+ self.messages,
+ [
+ "[test_deprecation 1.2.3] object test_deprecation.moving_target has been moved to "
+ "data.deprecation.moving_target"
+ ],
+ )
+
class StructuredDeprecatedWarningsTest(TestCase):
def mock_warn(self, *args, **kwargs):
@@ -294,6 +428,21 @@
self.assertEqual(warning.operation, deprecation.DeprecationWarningOperation.DEPRECATED)
self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.CLASS)
+ self.assertEqual(warning.version, None)
+ self.assertEqual(warning.package, "logilab-common")
+
+ def test_class_deprecated_version(self):
+ class AnyClass(metaclass=deprecation.class_deprecated):
+ __deprecation_warning_version__ = "1.2.3"
+
+ AnyClass()
+ self.assertEqual(len(self.collected_warnings), 1)
+ warning = self.collected_warnings.pop()
+
+ self.assertEqual(warning.operation, deprecation.DeprecationWarningOperation.DEPRECATED)
+ self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.CLASS)
+ self.assertEqual(warning.version, "1.2.3")
+ self.assertEqual(warning.package, "logilab-common")
def test_class_renamed(self):
class AnyClass:
@@ -309,6 +458,25 @@
self.assertEqual(warning.old_name, "OldClass")
self.assertEqual(warning.new_name, "AnyClass")
self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.CLASS)
+ self.assertEqual(warning.version, None)
+ self.assertEqual(warning.package, "logilab-common")
+
+ def test_class_renamed_version(self):
+ class AnyClass:
+ pass
+
+ OldClass = deprecation.class_renamed("OldClass", AnyClass, version="1.2.3")
+
+ OldClass()
+ self.assertEqual(len(self.collected_warnings), 1)
+ warning = self.collected_warnings.pop()
+
+ self.assertEqual(warning.operation, deprecation.DeprecationWarningOperation.RENAMED)
+ self.assertEqual(warning.old_name, "OldClass")
+ self.assertEqual(warning.new_name, "AnyClass")
+ self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.CLASS)
+ self.assertEqual(warning.version, "1.2.3")
+ self.assertEqual(warning.package, "logilab-common")
def test_class_moved(self):
class AnyClass:
@@ -326,6 +494,8 @@
self.assertEqual(warning.old_name, "OldName")
self.assertEqual(warning.new_name, "AnyClass")
self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.CLASS)
+ self.assertEqual(warning.version, None)
+ self.assertEqual(warning.package, "logilab-common")
self.collected_warnings = []
@@ -342,6 +512,45 @@
self.assertEqual(warning.old_name, "AnyClass")
self.assertEqual(warning.new_name, "AnyClass")
self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.CLASS)
+ self.assertEqual(warning.version, None)
+ self.assertEqual(warning.package, "logilab-common")
+
+ def test_class_moved_version(self):
+ class AnyClass:
+ pass
+
+ OldClass = deprecation.class_moved(new_class=AnyClass, old_name="OldName", version="1.2.3")
+ OldClass()
+
+ self.assertEqual(len(self.collected_warnings), 1)
+ warning = self.collected_warnings.pop()
+
+ self.assertEqual(warning.operation, deprecation.DeprecationWarningOperation.MOVED)
+ self.assertEqual(warning.old_module, "test_deprecation")
+ self.assertEqual(warning.new_module, "test_deprecation")
+ self.assertEqual(warning.old_name, "OldName")
+ self.assertEqual(warning.new_name, "AnyClass")
+ self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.CLASS)
+ self.assertEqual(warning.version, "1.2.3")
+ self.assertEqual(warning.package, "logilab-common")
+
+ self.collected_warnings = []
+
+ AnyClass = deprecation.class_moved(new_class=AnyClass, version="1.2.3")
+
+ AnyClass()
+
+ self.assertEqual(len(self.collected_warnings), 1)
+ warning = self.collected_warnings.pop()
+
+ self.assertEqual(warning.operation, deprecation.DeprecationWarningOperation.MOVED)
+ self.assertEqual(warning.old_module, "test_deprecation")
+ self.assertEqual(warning.new_module, "test_deprecation")
+ self.assertEqual(warning.old_name, "AnyClass")
+ self.assertEqual(warning.new_name, "AnyClass")
+ self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.CLASS)
+ self.assertEqual(warning.version, "1.2.3")
+ self.assertEqual(warning.package, "logilab-common")
def test_deprecated_func(self):
any_func = deprecation.callable_deprecated()(self.mk_func())
@@ -352,6 +561,8 @@
self.assertEqual(warning.operation, deprecation.DeprecationWarningOperation.DEPRECATED)
self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.CALLABLE)
+ self.assertEqual(warning.version, None)
+ self.assertEqual(warning.package, "logilab-common")
any_func = deprecation.callable_deprecated("message")(self.mk_func())
any_func()
@@ -361,6 +572,31 @@
self.assertEqual(warning.operation, deprecation.DeprecationWarningOperation.DEPRECATED)
self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.CALLABLE)
+ self.assertEqual(warning.version, None)
+ self.assertEqual(warning.package, "logilab-common")
+
+ def test_deprecated_func_version(self):
+ any_func = deprecation.callable_deprecated(version="1.2.3")(self.mk_func())
+ any_func()
+
+ self.assertEqual(len(self.collected_warnings), 1)
+ warning = self.collected_warnings.pop()
+
+ self.assertEqual(warning.operation, deprecation.DeprecationWarningOperation.DEPRECATED)
+ self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.CALLABLE)
+ self.assertEqual(warning.version, "1.2.3")
+ self.assertEqual(warning.package, "logilab-common")
+
+ any_func = deprecation.callable_deprecated("message", version="1.2.3")(self.mk_func())
+ any_func()
+
+ self.assertEqual(len(self.collected_warnings), 1)
+ warning = self.collected_warnings.pop()
+
+ self.assertEqual(warning.operation, deprecation.DeprecationWarningOperation.DEPRECATED)
+ self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.CALLABLE)
+ self.assertEqual(warning.version, "1.2.3")
+ self.assertEqual(warning.package, "logilab-common")
def test_deprecated_decorator(self):
@deprecation.callable_deprecated()
@@ -374,6 +610,8 @@
self.assertEqual(warning.operation, deprecation.DeprecationWarningOperation.DEPRECATED)
self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.CALLABLE)
+ self.assertEqual(warning.version, None)
+ self.assertEqual(warning.package, "logilab-common")
@deprecation.callable_deprecated("message")
def any_func():
@@ -386,6 +624,37 @@
self.assertEqual(warning.operation, deprecation.DeprecationWarningOperation.DEPRECATED)
self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.CALLABLE)
+ self.assertEqual(warning.version, None)
+ self.assertEqual(warning.package, "logilab-common")
+
+ def test_deprecated_decorator_version(self):
+ @deprecation.callable_deprecated(version="1.2.3")
+ def any_func():
+ pass
+
+ any_func()
+
+ self.assertEqual(len(self.collected_warnings), 1)
+ warning = self.collected_warnings.pop()
+
+ self.assertEqual(warning.operation, deprecation.DeprecationWarningOperation.DEPRECATED)
+ self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.CALLABLE)
+ self.assertEqual(warning.version, "1.2.3")
+ self.assertEqual(warning.package, "logilab-common")
+
+ @deprecation.callable_deprecated("message", version="1.2.3")
+ def any_func():
+ pass
+
+ any_func()
+
+ self.assertEqual(len(self.collected_warnings), 1)
+ warning = self.collected_warnings.pop()
+
+ self.assertEqual(warning.operation, deprecation.DeprecationWarningOperation.DEPRECATED)
+ self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.CALLABLE)
+ self.assertEqual(warning.version, "1.2.3")
+ self.assertEqual(warning.package, "logilab-common")
def test_attribute_renamed(self):
@deprecation.attribute_renamed(old_name="old", new_name="new")
@@ -404,6 +673,52 @@
self.assertEqual(warning.old_name, "old")
self.assertEqual(warning.new_name, "new")
self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.ATTRIBUTE)
+ self.assertEqual(warning.version, None)
+ self.assertEqual(warning.package, "logilab-common")
+
+ some_class.old = 43
+
+ self.assertEqual(len(self.collected_warnings), 1)
+ warning = self.collected_warnings.pop()
+
+ self.assertEqual(warning.operation, deprecation.DeprecationWarningOperation.RENAMED)
+ self.assertEqual(warning.old_name, "old")
+ self.assertEqual(warning.new_name, "new")
+ self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.ATTRIBUTE)
+ self.assertEqual(warning.version, None)
+ self.assertEqual(warning.package, "logilab-common")
+
+ del some_class.old
+
+ self.assertEqual(len(self.collected_warnings), 1)
+ warning = self.collected_warnings.pop()
+
+ self.assertEqual(warning.operation, deprecation.DeprecationWarningOperation.RENAMED)
+ self.assertEqual(warning.old_name, "old")
+ self.assertEqual(warning.new_name, "new")
+ self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.ATTRIBUTE)
+ self.assertEqual(warning.version, None)
+ self.assertEqual(warning.package, "logilab-common")
+
+ def test_attribute_renamed_version(self):
+ @deprecation.attribute_renamed(old_name="old", new_name="new", version="1.2.3")
+ class SomeClass:
+ def __init__(self):
+ self.new = 42
+
+ some_class = SomeClass()
+
+ some_class.old == some_class.new # trigger warning
+
+ self.assertEqual(len(self.collected_warnings), 1)
+ warning = self.collected_warnings.pop()
+
+ self.assertEqual(warning.operation, deprecation.DeprecationWarningOperation.RENAMED)
+ self.assertEqual(warning.old_name, "old")
+ self.assertEqual(warning.new_name, "new")
+ self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.ATTRIBUTE)
+ self.assertEqual(warning.version, "1.2.3")
+ self.assertEqual(warning.package, "logilab-common")
some_class.old = 43
@@ -414,6 +729,8 @@
self.assertEqual(warning.old_name, "old")
self.assertEqual(warning.new_name, "new")
self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.ATTRIBUTE)
+ self.assertEqual(warning.version, "1.2.3")
+ self.assertEqual(warning.package, "logilab-common")
del some_class.old
@@ -424,6 +741,8 @@
self.assertEqual(warning.old_name, "old")
self.assertEqual(warning.new_name, "new")
self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.ATTRIBUTE)
+ self.assertEqual(warning.version, "1.2.3")
+ self.assertEqual(warning.package, "logilab-common")
def test_argument_renamed(self):
@deprecation.argument_renamed(old_name="old", new_name="new")
@@ -439,6 +758,25 @@
self.assertEqual(warning.old_name, "old")
self.assertEqual(warning.new_name, "new")
self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.ARGUMENT)
+ self.assertEqual(warning.version, None)
+ self.assertEqual(warning.package, "logilab-common")
+
+ def test_argument_renamed_version(self):
+ @deprecation.argument_renamed(old_name="old", new_name="new", version="1.2.3")
+ def some_function(new):
+ return new
+
+ some_function(old=42)
+
+ self.assertEqual(len(self.collected_warnings), 1)
+ warning = self.collected_warnings.pop()
+
+ self.assertEqual(warning.operation, deprecation.DeprecationWarningOperation.RENAMED)
+ self.assertEqual(warning.old_name, "old")
+ self.assertEqual(warning.new_name, "new")
+ self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.ARGUMENT)
+ self.assertEqual(warning.version, "1.2.3")
+ self.assertEqual(warning.package, "logilab-common")
def test_argument_removed(self):
@deprecation.argument_removed("old")
@@ -453,6 +791,24 @@
self.assertEqual(warning.operation, deprecation.DeprecationWarningOperation.REMOVED)
self.assertEqual(warning.name, "old")
self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.ARGUMENT)
+ self.assertEqual(warning.version, None)
+ self.assertEqual(warning.package, "logilab-common")
+
+ def test_argument_removed_version(self):
+ @deprecation.argument_removed("old", version="1.2.3")
+ def some_function(new):
+ return new
+
+ some_function(new=10, old=20)
+
+ self.assertEqual(len(self.collected_warnings), 1)
+ warning = self.collected_warnings.pop()
+
+ self.assertEqual(warning.operation, deprecation.DeprecationWarningOperation.REMOVED)
+ self.assertEqual(warning.name, "old")
+ self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.ARGUMENT)
+ self.assertEqual(warning.version, "1.2.3")
+ self.assertEqual(warning.package, "logilab-common")
def test_callable_renamed(self):
def any_func():
@@ -468,6 +824,25 @@
self.assertEqual(warning.old_name, "old_func")
self.assertEqual(warning.new_name, "any_func")
self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.CALLABLE)
+ self.assertEqual(warning.version, None)
+ self.assertEqual(warning.package, "logilab-common")
+
+ def test_callable_renamed_version(self):
+ def any_func():
+ pass
+
+ old_func = deprecation.callable_renamed("old_func", any_func, version="1.2.3")
+ old_func()
+
+ self.assertEqual(len(self.collected_warnings), 1)
+ warning = self.collected_warnings.pop()
+
+ self.assertEqual(warning.operation, deprecation.DeprecationWarningOperation.RENAMED)
+ self.assertEqual(warning.old_name, "old_func")
+ self.assertEqual(warning.new_name, "any_func")
+ self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.CALLABLE)
+ self.assertEqual(warning.version, "1.2.3")
+ self.assertEqual(warning.package, "logilab-common")
def test_callable_moved(self):
module = "data.deprecation"
@@ -483,6 +858,25 @@
self.assertEqual(warning.old_name, "moving_target")
self.assertEqual(warning.new_name, "moving_target")
self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.CALLABLE)
+ self.assertEqual(warning.version, None)
+ self.assertEqual(warning.package, "logilab-common")
+
+ def test_callable_moved_version(self):
+ module = "data.deprecation"
+ moving_target = deprecation.callable_moved(module, "moving_target", version="1.2.3")
+ moving_target()
+
+ self.assertEqual(len(self.collected_warnings), 1)
+ warning = self.collected_warnings.pop()
+
+ self.assertEqual(warning.operation, deprecation.DeprecationWarningOperation.MOVED)
+ self.assertEqual(warning.old_module, "test_deprecation")
+ self.assertEqual(warning.new_module, "data.deprecation")
+ self.assertEqual(warning.old_name, "moving_target")
+ self.assertEqual(warning.new_name, "moving_target")
+ self.assertEqual(warning.kind, deprecation.DeprecationWarningKind.CALLABLE)
+ self.assertEqual(warning.version, "1.2.3")
+ self.assertEqual(warning.package, "logilab-common")
class DeprecatedWarningsTracebackLocationTest(TestCase):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-1.8.3/test/test_testlib.py new/logilab-common-1.9.7/test/test_testlib.py
--- old/logilab-common-1.8.3/test/test_testlib.py 2022-01-04 15:03:47.000000000 +0100
+++ new/logilab-common-1.9.7/test/test_testlib.py 2022-06-07 16:22:38.000000000 +0200
@@ -501,7 +501,7 @@
("FooTC.whatever", 0),
]
for pattern, expected_count in data:
- yield self.assertRunCount, pattern, self.module, expected_count
+ self.assertRunCount(pattern, self.module, expected_count)
def test_collect_with_pattern(self):
data = [
@@ -516,7 +516,7 @@
("ab", 0),
]
for pattern, expected_count in data:
- yield self.assertRunCount, pattern, self.module, expected_count
+ self.assertRunCount(pattern, self.module, expected_count)
def test_testcase_with_custom_metaclass(self):
class mymetaclass(type):
@@ -553,7 +553,7 @@
("MyTestCase.whatever", 0),
]
for pattern, expected_count in data:
- yield self.assertRunCount, pattern, MyMod, expected_count
+ self.assertRunCount(pattern, MyMod, expected_count)
def test_collect_everything_and_skipped_patterns(self):
testdata = [
@@ -562,7 +562,7 @@
(["foo", "bar"], 0),
]
for skipped, expected_count in testdata:
- yield self.assertRunCount, None, self.module, expected_count, skipped
+ self.assertRunCount(None, self.module, expected_count, skipped)
def test_collect_specific_pattern_and_skip_some(self):
testdata = [
@@ -571,7 +571,7 @@
("bar", ["bar"], 0),
]
for runpattern, skipped, expected_count in testdata:
- yield self.assertRunCount, runpattern, self.module, expected_count, skipped
+ self.assertRunCount(runpattern, self.module, expected_count, skipped)
def test_skip_classname(self):
testdata = [
@@ -579,7 +579,7 @@
(["FooTC"], 1),
]
for skipped, expected_count in testdata:
- yield self.assertRunCount, None, self.module, expected_count, skipped
+ self.assertRunCount(None, self.module, expected_count, skipped)
def test_skip_classname_and_specific_collect(self):
testdata = [
@@ -587,7 +587,7 @@
("foo", ["FooTC"], 0),
]
for runpattern, skipped, expected_count in testdata:
- yield self.assertRunCount, runpattern, self.module, expected_count, skipped
+ self.assertRunCount(runpattern, self.module, expected_count, skipped)
def test_nonregr_dotted_path(self):
self.assertRunCount("FooTC.test_foo", self.module, 2)
@@ -615,7 +615,7 @@
("odd", 0),
]
for pattern, expected_count in data:
- yield self.assertRunCount, pattern, MyMod, expected_count
+ self.assertRunCount(pattern, MyMod, expected_count)
def test_nonregr_class_skipped_option(self):
class MyMod:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-1.8.3/test/test_textutils.py new/logilab-common-1.9.7/test/test_textutils.py
--- old/logilab-common-1.8.3/test/test_textutils.py 2022-01-04 15:21:54.000000000 +0100
+++ new/logilab-common-1.9.7/test/test_textutils.py 2022-06-07 16:22:38.000000000 +0200
@@ -318,7 +318,7 @@
("Bordeaux\u2013M��rignac", "Bordeaux-Merignac"),
]
for input, output in data:
- yield self.assertEqual, tu.unormalize(input), output
+ self.assertEqual(tu.unormalize(input), output)
def test_unormalize_substitute(self):
self.assertEqual(tu.unormalize("ab \u8000 cd", substitute="_"), "ab _ cd")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/logilab-common-1.8.3/tox.ini new/logilab-common-1.9.7/tox.ini
--- old/logilab-common-1.8.3/tox.ini 2022-01-04 16:16:04.000000000 +0100
+++ new/logilab-common-1.9.7/tox.ini 2022-06-07 16:22:38.000000000 +0200
@@ -4,7 +4,7 @@
[testenv]
deps =
pytz
- pytest == 5.4.1
+ pytest
git+https://github.com/psycojoker/pytest-capture-deprecatedwarnings
commands=
{envpython} -m pytest test {posargs}
1
0
Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-line_profiler for openSUSE:Factory checked in at 2022-09-30 17:57:52
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-line_profiler (Old)
and /work/SRC/openSUSE:Factory/.python-line_profiler.new.2275 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-line_profiler"
Fri Sep 30 17:57:52 2022 rev:7 rq:1007071 version:3.5.1
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-line_profiler/python-line_profiler.changes 2022-01-08 23:24:05.898255107 +0100
+++ /work/SRC/openSUSE:Factory/.python-line_profiler.new.2275/python-line_profiler.changes 2022-09-30 17:58:10.389297184 +0200
@@ -1,0 +2,17 @@
+Thu Sep 29 14:50:17 UTC 2022 - Yogalakshmi Arunachalam <yarunachalam(a)suse.com>
+
+- Update to Version 3.5.2
+ * FIX: filepath test in is_ipython_kernel_cell for Windows #161
+ * ADD: setup.py now checks LINE_PROFILER_BUILD_METHOD to determine how to build binaries
+ * ADD: LineProfiler.add_function warns if an added function has a __wrapped__ attribute
+
+- Update to Version 3.5.1
+ * FIX: #19 line profiler now works on async functions again
+
+- Update to Version 3.5.0
+ * FIX: #109 kernprof fails to write to stdout if stdout was replaced
+ * FIX: Fixes max of an empty sequence error #118
+ * Make IPython optional
+ * FIX: #100 Exception raise ZeroDivisionError
+
+-------------------------------------------------------------------
Old:
----
line_profiler-3.4.0.tar.gz
New:
----
line_profiler-3.5.1.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-line_profiler.spec ++++++
--- /var/tmp/diff_new_pack.5WmRH9/_old 2022-09-30 17:58:10.789298039 +0200
+++ /var/tmp/diff_new_pack.5WmRH9/_new 2022-09-30 17:58:10.793298048 +0200
@@ -18,7 +18,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-line_profiler
-Version: 3.4.0
+Version: 3.5.1
Release: 0
Summary: Line-by-line profiler
License: BSD-3-Clause
++++++ line_profiler-3.4.0.tar.gz -> line_profiler-3.5.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/CHANGELOG.rst new/line_profiler-3.5.1/CHANGELOG.rst
--- old/line_profiler-3.4.0/CHANGELOG.rst 2021-12-30 02:01:08.000000000 +0100
+++ new/line_profiler-3.5.1/CHANGELOG.rst 2022-05-29 03:05:18.000000000 +0200
@@ -1,10 +1,21 @@
Changes
=======
+3.5.1
+~~~~~
+* FIX: #19 line profiler now works on async functions again
+
+3.5.0
+~~~~~
+* FIX: #109 kernprof fails to write to stdout if stdout was replaced
+* FIX: Fixes max of an empty sequence error #118
+* Make IPython optional
+* FIX: #100 Exception raise ZeroDivisionError
+
3.4.0
~~~~~
* Drop support for Python <= 3.5.x
-* FIX: #104 issue with new IPython kernels
+* FIX: #104 issue with new IPython kernels
3.3.1
~~~~~
@@ -18,7 +29,7 @@
3.2.6
~~~~~
* FIX: Update MANIFEST.in to package pyproj.toml and missing pyx file
-* CHANGE: Removed version experimental augmentation.
+* CHANGE: Removed version experimental augmentation.
3.2.5
~~~~~
@@ -40,7 +51,7 @@
3.2.0
~~~~~
-* Dropped 2.7 support, manylinux docker images no longer support 2.7
+* Dropped 2.7 support, manylinux docker images no longer support 2.7
* ENH: Add command line option to specify time unit and skip displaying
functions which have not been profiled.
* ENH: Unified versions of line_profiler and kernprof: kernprof version is now
@@ -110,4 +121,3 @@
~~~~~
* Initial release.
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/MANIFEST.in new/line_profiler-3.5.1/MANIFEST.in
--- old/line_profiler-3.4.0/MANIFEST.in 1970-01-01 01:00:00.000000000 +0100
+++ new/line_profiler-3.5.1/MANIFEST.in 2021-05-01 00:40:57.000000000 +0200
@@ -0,0 +1,14 @@
+include *.md
+include *.rst
+include *.py
+include *.txt
+include *.toml
+include run_tests.sh
+recursive-include requirements *.txt
+recursive-include tests *.py
+recursive-include line_profiler *.txt
+recursive-include line_profiler *.pyx
+recursive-include line_profiler *.pxd
+recursive-include line_profiler *.pyd
+recursive-include line_profiler *.c
+recursive-include line_profiler *.h
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/PKG-INFO new/line_profiler-3.5.1/PKG-INFO
--- old/line_profiler-3.4.0/PKG-INFO 2021-12-30 02:01:24.562787500 +0100
+++ new/line_profiler-3.5.1/PKG-INFO 2022-05-29 03:05:39.600498000 +0200
@@ -1,19 +1,11 @@
Metadata-Version: 2.1
Name: line_profiler
-Version: 3.4.0
+Version: 3.5.1
Summary: Line-by-line profiler.
Home-page: https://github.com/pyutils/line_profiler
Author: Robert Kern
Author-email: robert.kern(a)enthought.com
License: BSD
-Description: line_profiler will profile the time individual lines of code take to execute.
- The profiler is implemented in C via Cython in order to reduce the overhead of
- profiling.
-
- Also included is the script kernprof.py which can be used to conveniently
- profile Python applications and scripts either with line_profiler or with the
- function-level profiling tools in the Python standard library.
-
Keywords: timing,timer,profiling,profiler,line_profiler
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
@@ -31,5 +23,18 @@
Classifier: Topic :: Software Development
Description-Content-Type: text/x-rst
Provides-Extra: all
+Provides-Extra: ipython
Provides-Extra: tests
Provides-Extra: build
+License-File: LICENSE.txt
+License-File: LICENSE_Python.txt
+
+line_profiler will profile the time individual lines of code take to execute.
+The profiler is implemented in C via Cython in order to reduce the overhead of
+profiling.
+
+Also included is the script kernprof.py which can be used to conveniently
+profile Python applications and scripts either with line_profiler or with the
+function-level profiling tools in the Python standard library.
+
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/README.rst new/line_profiler-3.5.1/README.rst
--- old/line_profiler-3.4.0/README.rst 2021-12-30 02:01:08.000000000 +0100
+++ new/line_profiler-3.5.1/README.rst 2022-05-29 02:22:11.000000000 +0200
@@ -33,6 +33,10 @@
$ pip install line_profiler
+Installation while ensuring a compatible IPython version can also be installed using pip::
+
+ $ pip install line_profiler[ipython]
+
Source releases and any binaries can be downloaded from the PyPI link.
http://pypi.python.org/pypi/line_profiler
@@ -56,7 +60,7 @@
As of 2021-06-04 Linux (x86_64 and i686), OSX (10_9_x86_64), and Win32 (win32,
and amd64) binaries are available on pypi.
-Alternateively on windows you might consider using Christoph Gohlke's
+Alternatively on windows you might consider using Christoph Gohlke's
unofficial line-profiler
`precompiled win32 wheels <https://www.lfd.uci.edu/~gohlke/pythonlibs/#line_profiler>`_.
@@ -251,12 +255,35 @@
$ python -m pstats script_to_profile.py.prof
-Such files may also be viewed with graphical tools like SnakeViz_ and converted
-through pyprof2calltree_ to run on kcachegrind_ and compatible apps.
-.. _kcachegrind: http://kcachegrind.sourceforge.net/html/Home.html
+Such files may also be viewed with graphical tools. A list of 3rd party tools
+built on ``cProfile`` or ``line_profiler`` are as follows:
+
+* `pyprof2calltree <pyprof2calltree_>`_: converts profiling data to a format
+ that can be visualized using kcachegrind_ (linux only), wincachegrind_
+ (windows only, unmaintained), or qcachegrind_.
+
+* `Line Profiler GUI <qt_profiler_gui_>`_: Qt GUI for line_profiler.
+
+* `SnakeViz <SnakeViz_>`_: A web viewer for Python profiling data.
+
+* `SnakeRunner <SnakeRunner_>`_: A fork of RunSnakeRun_, ported to Python 3.
+
+* `Pycharm plugin <pycharm_line_profiler_plugin_>`_: A PyCharm plugin for line_profiler.
+
+* `Spyder plugin <spyder_line_profiler_plugin_>`_: A plugin to run line_profiler from within the Spyder IDE.
+
+
+.. _qcachegrind: https://sourceforge.net/projects/qcachegrindwin/
+.. _kcachegrind: https://kcachegrind.github.io/html/Home.html
+.. _wincachegrind: https://github.com/ceefour/wincachegrind
.. _pyprof2calltree: http://pypi.python.org/pypi/pyprof2calltree/
.. _SnakeViz: https://github.com/jiffyclub/snakeviz/
+.. _SnakeRunner: https://github.com/venthur/snakerunner
+.. _RunSnakeRun: https://pypi.org/project/RunSnakeRun/
+.. _qt_profiler_gui: https://github.com/Nodd/lineprofilergui
+.. _pycharm_line_profiler_plugin: https://plugins.jetbrains.com/plugin/16536-line-profiler
+.. _spyder_line_profiler_plugin: https://github.com/spyder-ide/spyder-line-profiler
Frequently Asked Questions
==========================
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/_skbuild/linux-x86_64-3.8/cmake-install/kernprof.py new/line_profiler-3.5.1/_skbuild/linux-x86_64-3.8/cmake-install/kernprof.py
--- old/line_profiler-3.4.0/_skbuild/linux-x86_64-3.8/cmake-install/kernprof.py 1970-01-01 01:00:00.000000000 +0100
+++ new/line_profiler-3.5.1/_skbuild/linux-x86_64-3.8/cmake-install/kernprof.py 2021-12-29 23:20:40.000000000 +0100
@@ -0,0 +1,236 @@
+#!/usr/bin/env python
+""" Script to conveniently run profilers on code in a variety of circumstances.
+"""
+import builtins
+import functools
+import os
+import sys
+from argparse import ArgumentError, ArgumentParser
+
+# NOTE: This version needs to be manually maintained with the line_profiler
+# __version__ for now.
+__version__ = '3.4.0'
+
+# Guard the import of cProfile such that 3.x people
+# without lsprof can still use this script.
+try:
+ from cProfile import Profile
+except ImportError:
+ try:
+ from lsprof import Profile
+ except ImportError:
+ from profile import Profile
+
+
+def execfile(filename, globals=None, locals=None):
+ """ Python 3.x doesn't have 'execfile' builtin """
+ with open(filename, 'rb') as f:
+ exec(compile(f.read(), filename, 'exec'), globals, locals)
+# =====================================
+
+
+CO_GENERATOR = 0x0020
+
+
+def is_generator(f):
+ """ Return True if a function is a generator.
+ """
+ isgen = (f.__code__.co_flags & CO_GENERATOR) != 0
+ return isgen
+
+
+class ContextualProfile(Profile):
+ """ A subclass of Profile that adds a context manager for Python
+ 2.5 with: statements and a decorator.
+ """
+
+ def __init__(self, *args, **kwds):
+ super().__init__(*args, **kwds)
+ self.enable_count = 0
+
+ def enable_by_count(self, subcalls=True, builtins=True):
+ """ Enable the profiler if it hasn't been enabled before.
+ """
+ if self.enable_count == 0:
+ self.enable(subcalls=subcalls, builtins=builtins)
+ self.enable_count += 1
+
+ def disable_by_count(self):
+ """ Disable the profiler if the number of disable requests matches the
+ number of enable requests.
+ """
+ if self.enable_count > 0:
+ self.enable_count -= 1
+ if self.enable_count == 0:
+ self.disable()
+
+ def __call__(self, func):
+ """ Decorate a function to start the profiler on function entry and stop
+ it on function exit.
+ """
+ # FIXME: refactor this into a utility function so that both it and
+ # line_profiler can use it.
+ if is_generator(func):
+ wrapper = self.wrap_generator(func)
+ else:
+ wrapper = self.wrap_function(func)
+ return wrapper
+
+ # FIXME: refactor this stuff so that both LineProfiler and
+ # ContextualProfile can use the same implementation.
+ def wrap_generator(self, func):
+ """ Wrap a generator to profile it.
+ """
+ @functools.wraps(func)
+ def wrapper(*args, **kwds):
+ g = func(*args, **kwds)
+ # The first iterate will not be a .send()
+ self.enable_by_count()
+ try:
+ item = next(g)
+ except StopIteration:
+ return
+ finally:
+ self.disable_by_count()
+ input = (yield item)
+ # But any following one might be.
+ while True:
+ self.enable_by_count()
+ try:
+ item = g.send(input)
+ except StopIteration:
+ return
+ finally:
+ self.disable_by_count()
+ input = (yield item)
+ return wrapper
+
+ def wrap_function(self, func):
+ """ Wrap a function to profile it.
+ """
+ @functools.wraps(func)
+ def wrapper(*args, **kwds):
+ self.enable_by_count()
+ try:
+ result = func(*args, **kwds)
+ finally:
+ self.disable_by_count()
+ return result
+ return wrapper
+
+ def __enter__(self):
+ self.enable_by_count()
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ self.disable_by_count()
+
+
+def find_script(script_name):
+ """ Find the script.
+
+ If the input is not a file, then $PATH will be searched.
+ """
+ if os.path.isfile(script_name):
+ return script_name
+ path = os.getenv('PATH', os.defpath).split(os.pathsep)
+ for dir in path:
+ if dir == '':
+ continue
+ fn = os.path.join(dir, script_name)
+ if os.path.isfile(fn):
+ return fn
+
+ sys.stderr.write('Could not find script %s\n' % script_name)
+ raise SystemExit(1)
+
+
+def main(args=None):
+ def positive_float(value):
+ val = float(value)
+ if val <= 0:
+ raise ArgumentError
+ return val
+
+ parser = ArgumentParser(description='Run and profile a python script.')
+ parser.add_argument('-V', '--version', action='version', version=__version__)
+ parser.add_argument('-l', '--line-by-line', action='store_true',
+ help='Use the line-by-line profiler instead of cProfile. Implies --builtin.')
+ parser.add_argument('-b', '--builtin', action='store_true',
+ help="Put 'profile' in the builtins. Use 'profile.enable()'/'.disable()', "
+ "'@profile' to decorate functions, or 'with profile:' to profile a "
+ 'section of code.')
+ parser.add_argument('-o', '--outfile',
+ help="Save stats to <outfile> (default: 'scriptname.lprof' with "
+ "--line-by-line, 'scriptname.prof' without)")
+ parser.add_argument('-s', '--setup',
+ help='Code to execute before the code to profile')
+ parser.add_argument('-v', '--view', action='store_true',
+ help='View the results of the profile in addition to saving it')
+ parser.add_argument('-u', '--unit', default='1e-6', type=positive_float,
+
+ help='Output unit (in seconds) in which the timing info is '
+ 'displayed (default: 1e-6)')
+ parser.add_argument('-z', '--skip-zero', action='store_true',
+ help='Hide functions which have not been called')
+
+ parser.add_argument('script', help='The python script file to run')
+ parser.add_argument('args', nargs='...', help='Optional script arguments')
+
+ options = parser.parse_args(args)
+
+ if not options.outfile:
+ extension = 'lprof' if options.line_by_line else 'prof'
+ options.outfile = '%s.%s' % (os.path.basename(options.script), extension)
+
+ sys.argv = [options.script] + options.args
+ if options.setup is not None:
+ # Run some setup code outside of the profiler. This is good for large
+ # imports.
+ setup_file = find_script(options.setup)
+ __file__ = setup_file
+ __name__ = '__main__'
+ # Make sure the script's directory is on sys.path instead of just
+ # kernprof.py's.
+ sys.path.insert(0, os.path.dirname(setup_file))
+ ns = locals()
+ execfile(setup_file, ns, ns)
+
+ if options.line_by_line:
+ import line_profiler
+ prof = line_profiler.LineProfiler()
+ options.builtin = True
+ else:
+ prof = ContextualProfile()
+ if options.builtin:
+ builtins.__dict__['profile'] = prof
+
+ script_file = find_script(options.script)
+ __file__ = script_file
+ __name__ = '__main__'
+ # Make sure the script's directory is on sys.path instead of just
+ # kernprof.py's.
+ sys.path.insert(0, os.path.dirname(script_file))
+
+ try:
+ try:
+ execfile_ = execfile
+ ns = locals()
+ if options.builtin:
+ execfile(script_file, ns, ns)
+ else:
+ prof.runctx('execfile_(%r, globals())' % (script_file,), ns, ns)
+ except (KeyboardInterrupt, SystemExit):
+ pass
+ finally:
+ prof.dump_stats(options.outfile)
+ print('Wrote profile results to %s' % options.outfile)
+ if options.view:
+ if isinstance(prof, ContextualProfile):
+ prof.print_stats()
+ else:
+ prof.print_stats(output_unit=options.unit,
+ stripzeros=options.skip_zero)
+
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/_skbuild/linux-x86_64-3.8/cmake-install/line_profiler/__init__.py new/line_profiler-3.5.1/_skbuild/linux-x86_64-3.8/cmake-install/line_profiler/__init__.py
--- old/line_profiler-3.4.0/_skbuild/linux-x86_64-3.8/cmake-install/line_profiler/__init__.py 1970-01-01 01:00:00.000000000 +0100
+++ new/line_profiler-3.5.1/_skbuild/linux-x86_64-3.8/cmake-install/line_profiler/__init__.py 2021-12-29 23:20:40.000000000 +0100
@@ -0,0 +1,22 @@
+"""
+The line_profiler modula for doing line-by-line profiling of functions
+"""
+__submodules__ = [
+ 'line_profiler',
+]
+
+__autogen__ = """
+mkinit ./line_profiler/__init__.py --relative
+mkinit ./line_profiler/__init__.py --relative -w
+"""
+
+
+from .line_profiler import __version__
+
+from .line_profiler import (LineProfiler, LineProfilerMagics,
+ load_ipython_extension, load_stats, main,
+ show_func, show_text,)
+
+__all__ = ['LineProfiler', 'LineProfilerMagics', 'line_profiler',
+ 'load_ipython_extension', 'load_stats', 'main', 'show_func',
+ 'show_text', '__version__']
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/_skbuild/linux-x86_64-3.8/cmake-install/line_profiler/__main__.py new/line_profiler-3.5.1/_skbuild/linux-x86_64-3.8/cmake-install/line_profiler/__main__.py
--- old/line_profiler-3.4.0/_skbuild/linux-x86_64-3.8/cmake-install/line_profiler/__main__.py 1970-01-01 01:00:00.000000000 +0100
+++ new/line_profiler-3.5.1/_skbuild/linux-x86_64-3.8/cmake-install/line_profiler/__main__.py 2021-12-29 23:20:40.000000000 +0100
@@ -0,0 +1,4 @@
+from .line_profiler import main
+
+if __name__ == '__main__':
+ main()
Binary files old/line_profiler-3.4.0/_skbuild/linux-x86_64-3.8/cmake-install/line_profiler/_line_profiler.cpython-38-x86_64-linux-gnu.so and new/line_profiler-3.5.1/_skbuild/linux-x86_64-3.8/cmake-install/line_profiler/_line_profiler.cpython-38-x86_64-linux-gnu.so differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/_skbuild/linux-x86_64-3.8/cmake-install/line_profiler/line_profiler.py new/line_profiler-3.5.1/_skbuild/linux-x86_64-3.8/cmake-install/line_profiler/line_profiler.py
--- old/line_profiler-3.4.0/_skbuild/linux-x86_64-3.8/cmake-install/line_profiler/line_profiler.py 1970-01-01 01:00:00.000000000 +0100
+++ new/line_profiler-3.5.1/_skbuild/linux-x86_64-3.8/cmake-install/line_profiler/line_profiler.py 2021-12-29 23:20:40.000000000 +0100
@@ -0,0 +1,421 @@
+#!/usr/bin/env python
+import pickle
+import functools
+import inspect
+import linecache
+import tempfile
+import os
+import sys
+from io import StringIO
+from argparse import ArgumentError, ArgumentParser
+
+from IPython.core.magic import (Magics, magics_class, line_magic)
+from IPython.core.page import page
+from IPython.utils.ipstruct import Struct
+from IPython.core.error import UsageError
+
+try:
+ from ._line_profiler import LineProfiler as CLineProfiler
+except ImportError as ex:
+ raise ImportError(
+ 'The line_profiler._line_profiler c-extension is not importable. '
+ f'Has it been compiled? Underlying error is ex={ex!r}'
+ )
+
+__version__ = '3.4.0'
+
+
+def is_coroutine(f):
+ return False
+
+
+CO_GENERATOR = 0x0020
+
+
+def is_generator(f):
+ """ Return True if a function is a generator.
+ """
+ isgen = (f.__code__.co_flags & CO_GENERATOR) != 0
+ return isgen
+
+
+class LineProfiler(CLineProfiler):
+ """ A profiler that records the execution times of individual lines.
+ """
+
+ def __call__(self, func):
+ """ Decorate a function to start the profiler on function entry and stop
+ it on function exit.
+ """
+ self.add_function(func)
+ if is_coroutine(func):
+ wrapper = self.wrap_coroutine(func)
+ elif is_generator(func):
+ wrapper = self.wrap_generator(func)
+ else:
+ wrapper = self.wrap_function(func)
+ return wrapper
+
+ def wrap_generator(self, func):
+ """ Wrap a generator to profile it.
+ """
+ @functools.wraps(func)
+ def wrapper(*args, **kwds):
+ g = func(*args, **kwds)
+ # The first iterate will not be a .send()
+ self.enable_by_count()
+ try:
+ item = next(g)
+ except StopIteration:
+ return
+ finally:
+ self.disable_by_count()
+ input_ = (yield item)
+ # But any following one might be.
+ while True:
+ self.enable_by_count()
+ try:
+ item = g.send(input_)
+ except StopIteration:
+ return
+ finally:
+ self.disable_by_count()
+ input_ = (yield item)
+ return wrapper
+
+ def wrap_function(self, func):
+ """ Wrap a function to profile it.
+ """
+ @functools.wraps(func)
+ def wrapper(*args, **kwds):
+ self.enable_by_count()
+ try:
+ result = func(*args, **kwds)
+ finally:
+ self.disable_by_count()
+ return result
+ return wrapper
+
+ def dump_stats(self, filename):
+ """ Dump a representation of the data to a file as a pickled LineStats
+ object from `get_stats()`.
+ """
+ lstats = self.get_stats()
+ with open(filename, 'wb') as f:
+ pickle.dump(lstats, f, pickle.HIGHEST_PROTOCOL)
+
+ def print_stats(self, stream=None, output_unit=None, stripzeros=False):
+ """ Show the gathered statistics.
+ """
+ lstats = self.get_stats()
+ show_text(lstats.timings, lstats.unit, output_unit=output_unit, stream=stream, stripzeros=stripzeros)
+
+ def run(self, cmd):
+ """ Profile a single executable statment in the main namespace.
+ """
+ import __main__
+ main_dict = __main__.__dict__
+ return self.runctx(cmd, main_dict, main_dict)
+
+ def runctx(self, cmd, globals, locals):
+ """ Profile a single executable statement in the given namespaces.
+ """
+ self.enable_by_count()
+ try:
+ exec(cmd, globals, locals)
+ finally:
+ self.disable_by_count()
+ return self
+
+ def runcall(self, func, *args, **kw):
+ """ Profile a single function call.
+ """
+ self.enable_by_count()
+ try:
+ return func(*args, **kw)
+ finally:
+ self.disable_by_count()
+
+ def add_module(self, mod):
+ """ Add all the functions in a module and its classes.
+ """
+ from inspect import isclass, isfunction
+
+ nfuncsadded = 0
+ for item in mod.__dict__.values():
+ if isclass(item):
+ for k, v in item.__dict__.items():
+ if isfunction(v):
+ self.add_function(v)
+ nfuncsadded += 1
+ elif isfunction(item):
+ self.add_function(item)
+ nfuncsadded += 1
+
+ return nfuncsadded
+
+
+def is_ipython_kernel_cell(filename):
+ """ Return True if a filename corresponds to a Jupyter Notebook cell
+ """
+ return (
+ filename.startswith('<ipython-input-') or
+ filename.startswith(tempfile.gettempdir() + '/ipykernel_') or
+ filename.startswith(tempfile.gettempdir() + '/xpython_')
+ )
+
+
+def show_func(filename, start_lineno, func_name, timings, unit,
+ output_unit=None, stream=None, stripzeros=False):
+ """ Show results for a single function.
+ """
+ if stream is None:
+ stream = sys.stdout
+
+ template = '%6s %9s %12s %8s %8s %-s'
+ d = {}
+ total_time = 0.0
+ linenos = []
+ for lineno, nhits, time in timings:
+ total_time += time
+ linenos.append(lineno)
+
+ if stripzeros and total_time == 0:
+ return
+
+ if output_unit is None:
+ output_unit = unit
+ scalar = unit / output_unit
+
+ stream.write('Total time: %g s\n' % (total_time * unit))
+ if os.path.exists(filename) or is_ipython_kernel_cell(filename):
+ stream.write(f'File: {filename}\n')
+ stream.write(f'Function: {func_name} at line {start_lineno}\n')
+ if os.path.exists(filename):
+ # Clear the cache to ensure that we get up-to-date results.
+ linecache.clearcache()
+ all_lines = linecache.getlines(filename)
+ sublines = inspect.getblock(all_lines[start_lineno - 1:])
+ else:
+ stream.write('\n')
+ stream.write(f'Could not find file {filename}\n')
+ stream.write('Are you sure you are running this program from the same directory\n')
+ stream.write('that you ran the profiler from?\n')
+ stream.write("Continuing without the function's contents.\n")
+ # Fake empty lines so we can see the timings, if not the code.
+ nlines = max(linenos) - min(min(linenos), start_lineno) + 1
+ sublines = [''] * nlines
+ for lineno, nhits, time in timings:
+ d[lineno] = (nhits,
+ '%5.1f' % (time * scalar),
+ '%5.1f' % (float(time) * scalar / nhits),
+ '%5.1f' % (100 * time / total_time) )
+ linenos = range(start_lineno, start_lineno + len(sublines))
+ empty = ('', '', '', '')
+ header = template % ('Line #', 'Hits', 'Time', 'Per Hit', '% Time',
+ 'Line Contents')
+ stream.write('\n')
+ stream.write(header)
+ stream.write('\n')
+ stream.write('=' * len(header))
+ stream.write('\n')
+ for lineno, line in zip(linenos, sublines):
+ nhits, time, per_hit, percent = d.get(lineno, empty)
+ txt = template % (lineno, nhits, time, per_hit, percent,
+ line.rstrip('\n').rstrip('\r'))
+ stream.write(txt)
+ stream.write('\n')
+ stream.write('\n')
+
+
+def show_text(stats, unit, output_unit=None, stream=None, stripzeros=False):
+ """ Show text for the given timings.
+ """
+ if stream is None:
+ stream = sys.stdout
+
+ if output_unit is not None:
+ stream.write('Timer unit: %g s\n\n' % output_unit)
+ else:
+ stream.write('Timer unit: %g s\n\n' % unit)
+
+ for (fn, lineno, name), timings in sorted(stats.items()):
+ show_func(fn, lineno, name, stats[fn, lineno, name], unit,
+ output_unit=output_unit, stream=stream,
+ stripzeros=stripzeros)
+
+
+@magics_class
+class LineProfilerMagics(Magics):
+
+ @line_magic
+ def lprun(self, parameter_s=''):
+ """ Execute a statement under the line-by-line profiler from the
+ line_profiler module.
+
+ Usage:
+ %lprun -f func1 -f func2 <statement>
+
+ The given statement (which doesn't require quote marks) is run via the
+ LineProfiler. Profiling is enabled for the functions specified by the -f
+ options. The statistics will be shown side-by-side with the code through the
+ pager once the statement has completed.
+
+ Options:
+
+ -f <function>: LineProfiler only profiles functions and methods it is told
+ to profile. This option tells the profiler about these functions. Multiple
+ -f options may be used. The argument may be any expression that gives
+ a Python function or method object. However, one must be careful to avoid
+ spaces that may confuse the option parser.
+
+ -m <module>: Get all the functions/methods in a module
+
+ One or more -f or -m options are required to get any useful results.
+
+ -D <filename>: dump the raw statistics out to a pickle file on disk. The
+ usual extension for this is ".lprof". These statistics may be viewed later
+ by running line_profiler.py as a script.
+
+ -T <filename>: dump the text-formatted statistics with the code side-by-side
+ out to a text file.
+
+ -r: return the LineProfiler object after it has completed profiling.
+
+ -s: strip out all entries from the print-out that have zeros.
+
+ -u: specify time unit for the print-out in seconds.
+ """
+
+ # Escape quote markers.
+ opts_def = Struct(D=[''], T=[''], f=[], m=[], u=None)
+ parameter_s = parameter_s.replace('"', r'\"').replace("'", r"\'")
+ opts, arg_str = self.parse_options(parameter_s, 'rsf:m:D:T:u:', list_all=True)
+ opts.merge(opts_def)
+
+ global_ns = self.shell.user_global_ns
+ local_ns = self.shell.user_ns
+
+ # Get the requested functions.
+ funcs = []
+ for name in opts.f:
+ try:
+ funcs.append(eval(name, global_ns, local_ns))
+ except Exception as e:
+ raise UsageError(f'Could not find module {name}.\n{e.__class__.__name__}: {e}')
+
+ profile = LineProfiler(*funcs)
+
+ # Get the modules, too
+ for modname in opts.m:
+ try:
+ mod = __import__(modname, fromlist=[''])
+ profile.add_module(mod)
+ except Exception as e:
+ raise UsageError(f'Could not find module {modname}.\n{e.__class__.__name__}: {e}')
+
+ if opts.u is not None:
+ try:
+ output_unit = float(opts.u[0])
+ except Exception:
+ raise TypeError('Timer unit setting must be a float.')
+ else:
+ output_unit = None
+
+ # Add the profiler to the builtins for @profile.
+ import builtins
+
+ if 'profile' in builtins.__dict__:
+ had_profile = True
+ old_profile = builtins.__dict__['profile']
+ else:
+ had_profile = False
+ old_profile = None
+ builtins.__dict__['profile'] = profile
+
+ try:
+ try:
+ profile.runctx(arg_str, global_ns, local_ns)
+ message = ''
+ except SystemExit:
+ message = """*** SystemExit exception caught in code being profiled."""
+ except KeyboardInterrupt:
+ message = ('*** KeyboardInterrupt exception caught in code being '
+ 'profiled.')
+ finally:
+ if had_profile:
+ builtins.__dict__['profile'] = old_profile
+
+ # Trap text output.
+ stdout_trap = StringIO()
+ profile.print_stats(stdout_trap, output_unit=output_unit, stripzeros='s' in opts)
+ output = stdout_trap.getvalue()
+ output = output.rstrip()
+
+ page(output)
+ print(message, end='')
+
+ dump_file = opts.D[0]
+ if dump_file:
+ profile.dump_stats(dump_file)
+ print(f'\n*** Profile stats pickled to file {dump_file!r}. {message}')
+
+ text_file = opts.T[0]
+ if text_file:
+ pfile = open(text_file, 'w')
+ pfile.write(output)
+ pfile.close()
+ print(f'\n*** Profile printout saved to text file {text_file!r}. {message}')
+
+ return_value = None
+ if 'r' in opts:
+ return_value = profile
+
+ return return_value
+
+
+def load_ipython_extension(ip):
+ """ API for IPython to recognize this module as an IPython extension.
+ """
+ ip.register_magics(LineProfilerMagics)
+
+
+def load_stats(filename):
+ """ Utility function to load a pickled LineStats object from a given
+ filename.
+ """
+ with open(filename, 'rb') as f:
+ return pickle.load(f)
+
+
+def main():
+ def positive_float(value):
+ val = float(value)
+ if val <= 0:
+ raise ArgumentError
+ return val
+
+ parser = ArgumentParser()
+ parser.add_argument('-V', '--version', action='version', version=__version__)
+ parser.add_argument(
+ '-u',
+ '--unit',
+ default='1e-6',
+ type=positive_float,
+ help='Output unit (in seconds) in which the timing info is displayed (default: 1e-6)',
+ )
+ parser.add_argument(
+ '-z',
+ '--skip-zero',
+ action='store_true',
+ help='Hide functions which have not been called',
+ )
+ parser.add_argument('profile_output', help='*.lprof file created by kernprof')
+
+ args = parser.parse_args()
+ lstats = load_stats(args.profile_output)
+ show_text(lstats.timings, lstats.unit, output_unit=args.unit, stripzeros=args.skip_zero)
+
+
+if __name__ == '__main__':
+ main()
Binary files old/line_profiler-3.4.0/_skbuild/linux-x86_64-3.9/cmake-install/line_profiler/_line_profiler.cpython-39-x86_64-linux-gnu.so and new/line_profiler-3.5.1/_skbuild/linux-x86_64-3.9/cmake-install/line_profiler/_line_profiler.cpython-39-x86_64-linux-gnu.so differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/kernprof.py new/line_profiler-3.5.1/kernprof.py
--- old/line_profiler-3.4.0/kernprof.py 2021-12-30 02:01:08.000000000 +0100
+++ new/line_profiler-3.5.1/kernprof.py 2022-05-29 03:05:18.000000000 +0200
@@ -9,7 +9,7 @@
# NOTE: This version needs to be manually maintained with the line_profiler
# __version__ for now.
-__version__ = '3.4.0'
+__version__ = '3.5.1'
# Guard the import of cProfile such that 3.x people
# without lsprof can still use this script.
@@ -211,6 +211,7 @@
# kernprof.py's.
sys.path.insert(0, os.path.dirname(script_file))
+ original_stdout = sys.stdout
try:
try:
execfile_ = execfile
@@ -226,10 +227,11 @@
print('Wrote profile results to %s' % options.outfile)
if options.view:
if isinstance(prof, ContextualProfile):
- prof.print_stats()
+ prof.print_stats(stream=original_stdout)
else:
prof.print_stats(output_unit=options.unit,
- stripzeros=options.skip_zero)
+ stripzeros=options.skip_zero,
+ stream=original_stdout)
if __name__ == '__main__':
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/line_profiler/__init__.py new/line_profiler-3.5.1/line_profiler/__init__.py
--- old/line_profiler-3.4.0/line_profiler/__init__.py 2021-12-30 02:01:08.000000000 +0100
+++ new/line_profiler-3.5.1/line_profiler/__init__.py 2022-04-01 08:53:10.000000000 +0200
@@ -3,6 +3,7 @@
"""
__submodules__ = [
'line_profiler',
+ 'ipython_extension',
]
__autogen__ = """
@@ -13,10 +14,10 @@
from .line_profiler import __version__
-from .line_profiler import (LineProfiler, LineProfilerMagics,
+from .line_profiler import (LineProfiler,
load_ipython_extension, load_stats, main,
show_func, show_text,)
-__all__ = ['LineProfiler', 'LineProfilerMagics', 'line_profiler',
+__all__ = ['LineProfiler', 'line_profiler',
'load_ipython_extension', 'load_stats', 'main', 'show_func',
'show_text', '__version__']
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/line_profiler/ipython_extension.py new/line_profiler-3.5.1/line_profiler/ipython_extension.py
--- old/line_profiler-3.4.0/line_profiler/ipython_extension.py 1970-01-01 01:00:00.000000000 +0100
+++ new/line_profiler-3.5.1/line_profiler/ipython_extension.py 2022-04-01 08:53:10.000000000 +0200
@@ -0,0 +1,143 @@
+from io import StringIO
+
+from IPython.core.magic import Magics, magics_class, line_magic
+from IPython.core.page import page
+from IPython.utils.ipstruct import Struct
+from IPython.core.error import UsageError
+
+from .line_profiler import LineProfiler
+
+
+@magics_class
+class LineProfilerMagics(Magics):
+ @line_magic
+ def lprun(self, parameter_s=""):
+ """ Execute a statement under the line-by-line profiler from the
+ line_profiler module.
+
+ Usage:
+ %lprun -f func1 -f func2 <statement>
+
+ The given statement (which doesn't require quote marks) is run via the
+ LineProfiler. Profiling is enabled for the functions specified by the -f
+ options. The statistics will be shown side-by-side with the code through the
+ pager once the statement has completed.
+
+ Options:
+
+ -f <function>: LineProfiler only profiles functions and methods it is told
+ to profile. This option tells the profiler about these functions. Multiple
+ -f options may be used. The argument may be any expression that gives
+ a Python function or method object. However, one must be careful to avoid
+ spaces that may confuse the option parser.
+
+ -m <module>: Get all the functions/methods in a module
+
+ One or more -f or -m options are required to get any useful results.
+
+ -D <filename>: dump the raw statistics out to a pickle file on disk. The
+ usual extension for this is ".lprof". These statistics may be viewed later
+ by running line_profiler.py as a script.
+
+ -T <filename>: dump the text-formatted statistics with the code side-by-side
+ out to a text file.
+
+ -r: return the LineProfiler object after it has completed profiling.
+
+ -s: strip out all entries from the print-out that have zeros.
+
+ -u: specify time unit for the print-out in seconds.
+ """
+
+ # Escape quote markers.
+ opts_def = Struct(D=[""], T=[""], f=[], m=[], u=None)
+ parameter_s = parameter_s.replace('"', r"\"").replace("'", r"\'")
+ opts, arg_str = self.parse_options(parameter_s, "rsf:m:D:T:u:", list_all=True)
+ opts.merge(opts_def)
+
+ global_ns = self.shell.user_global_ns
+ local_ns = self.shell.user_ns
+
+ # Get the requested functions.
+ funcs = []
+ for name in opts.f:
+ try:
+ funcs.append(eval(name, global_ns, local_ns))
+ except Exception as e:
+ raise UsageError(
+ f"Could not find module {name}.\n{e.__class__.__name__}: {e}"
+ )
+
+ profile = LineProfiler(*funcs)
+
+ # Get the modules, too
+ for modname in opts.m:
+ try:
+ mod = __import__(modname, fromlist=[""])
+ profile.add_module(mod)
+ except Exception as e:
+ raise UsageError(
+ f"Could not find module {modname}.\n{e.__class__.__name__}: {e}"
+ )
+
+ if opts.u is not None:
+ try:
+ output_unit = float(opts.u[0])
+ except Exception:
+ raise TypeError("Timer unit setting must be a float.")
+ else:
+ output_unit = None
+
+ # Add the profiler to the builtins for @profile.
+ import builtins
+
+ if "profile" in builtins.__dict__:
+ had_profile = True
+ old_profile = builtins.__dict__["profile"]
+ else:
+ had_profile = False
+ old_profile = None
+ builtins.__dict__["profile"] = profile
+
+ try:
+ try:
+ profile.runctx(arg_str, global_ns, local_ns)
+ message = ""
+ except SystemExit:
+ message = """*** SystemExit exception caught in code being profiled."""
+ except KeyboardInterrupt:
+ message = (
+ "*** KeyboardInterrupt exception caught in code being " "profiled."
+ )
+ finally:
+ if had_profile:
+ builtins.__dict__["profile"] = old_profile
+
+ # Trap text output.
+ stdout_trap = StringIO()
+ profile.print_stats(
+ stdout_trap, output_unit=output_unit, stripzeros="s" in opts
+ )
+ output = stdout_trap.getvalue()
+ output = output.rstrip()
+
+ page(output)
+ print(message, end="")
+
+ dump_file = opts.D[0]
+ if dump_file:
+ profile.dump_stats(dump_file)
+ print(f"\n*** Profile stats pickled to file {dump_file!r}. {message}")
+
+ text_file = opts.T[0]
+ if text_file:
+ pfile = open(text_file, "w")
+ pfile.write(output)
+ pfile.close()
+ print(f"\n*** Profile printout saved to text file {text_file!r}. {message}")
+
+ return_value = None
+ if "r" in opts:
+ return_value = profile
+
+ return return_value
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/line_profiler/line_profiler.py new/line_profiler-3.5.1/line_profiler/line_profiler.py
--- old/line_profiler-3.4.0/line_profiler/line_profiler.py 2021-12-30 02:01:08.000000000 +0100
+++ new/line_profiler-3.5.1/line_profiler/line_profiler.py 2022-05-29 03:05:18.000000000 +0200
@@ -6,14 +6,8 @@
import tempfile
import os
import sys
-from io import StringIO
from argparse import ArgumentError, ArgumentParser
-from IPython.core.magic import (Magics, magics_class, line_magic)
-from IPython.core.page import page
-from IPython.utils.ipstruct import Struct
-from IPython.core.error import UsageError
-
try:
from ._line_profiler import LineProfiler as CLineProfiler
except ImportError as ex:
@@ -22,11 +16,18 @@
f'Has it been compiled? Underlying error is ex={ex!r}'
)
-__version__ = '3.4.0'
+__version__ = '3.5.1'
+
+
+def load_ipython_extension(ip):
+ """ API for IPython to recognize this module as an IPython extension.
+ """
+ from .ipython_extension import LineProfilerMagics
+ ip.register_magics(LineProfilerMagics)
def is_coroutine(f):
- return False
+ return inspect.iscoroutinefunction(f)
CO_GENERATOR = 0x0020
@@ -56,6 +57,22 @@
wrapper = self.wrap_function(func)
return wrapper
+ def wrap_coroutine(self, func):
+ """
+ Wrap a Python 3.5 coroutine to profile it.
+ """
+
+ @functools.wraps(func)
+ async def wrapper(*args, **kwds):
+ self.enable_by_count()
+ try:
+ result = await func(*args, **kwds)
+ finally:
+ self.disable_by_count()
+ return result
+
+ return wrapper
+
def wrap_generator(self, func):
""" Wrap a generator to profile it.
"""
@@ -155,6 +172,8 @@
return nfuncsadded
+# This could be in the ipython_extension submodule,
+# but it doesn't depend on the IPython module so it's easier to just let it stay here.
def is_ipython_kernel_cell(filename):
""" Return True if a filename corresponds to a Jupyter Notebook cell
"""
@@ -203,13 +222,17 @@
stream.write('that you ran the profiler from?\n')
stream.write("Continuing without the function's contents.\n")
# Fake empty lines so we can see the timings, if not the code.
- nlines = max(linenos) - min(min(linenos), start_lineno) + 1
+ nlines = 1 if not linenos else max(linenos) - min(min(linenos), start_lineno) + 1
sublines = [''] * nlines
for lineno, nhits, time in timings:
+ if total_time == 0: # Happens rarely on empty function
+ percent = ''
+ else:
+ percent = '%5.1f' % (100 * time / total_time)
d[lineno] = (nhits,
'%5.1f' % (time * scalar),
'%5.1f' % (float(time) * scalar / nhits),
- '%5.1f' % (100 * time / total_time) )
+ percent)
linenos = range(start_lineno, start_lineno + len(sublines))
empty = ('', '', '', '')
header = template % ('Line #', 'Hits', 'Time', 'Per Hit', '% Time',
@@ -244,142 +267,6 @@
output_unit=output_unit, stream=stream,
stripzeros=stripzeros)
-
-@magics_class
-class LineProfilerMagics(Magics):
-
- @line_magic
- def lprun(self, parameter_s=''):
- """ Execute a statement under the line-by-line profiler from the
- line_profiler module.
-
- Usage:
- %lprun -f func1 -f func2 <statement>
-
- The given statement (which doesn't require quote marks) is run via the
- LineProfiler. Profiling is enabled for the functions specified by the -f
- options. The statistics will be shown side-by-side with the code through the
- pager once the statement has completed.
-
- Options:
-
- -f <function>: LineProfiler only profiles functions and methods it is told
- to profile. This option tells the profiler about these functions. Multiple
- -f options may be used. The argument may be any expression that gives
- a Python function or method object. However, one must be careful to avoid
- spaces that may confuse the option parser.
-
- -m <module>: Get all the functions/methods in a module
-
- One or more -f or -m options are required to get any useful results.
-
- -D <filename>: dump the raw statistics out to a pickle file on disk. The
- usual extension for this is ".lprof". These statistics may be viewed later
- by running line_profiler.py as a script.
-
- -T <filename>: dump the text-formatted statistics with the code side-by-side
- out to a text file.
-
- -r: return the LineProfiler object after it has completed profiling.
-
- -s: strip out all entries from the print-out that have zeros.
-
- -u: specify time unit for the print-out in seconds.
- """
-
- # Escape quote markers.
- opts_def = Struct(D=[''], T=[''], f=[], m=[], u=None)
- parameter_s = parameter_s.replace('"', r'\"').replace("'", r"\'")
- opts, arg_str = self.parse_options(parameter_s, 'rsf:m:D:T:u:', list_all=True)
- opts.merge(opts_def)
-
- global_ns = self.shell.user_global_ns
- local_ns = self.shell.user_ns
-
- # Get the requested functions.
- funcs = []
- for name in opts.f:
- try:
- funcs.append(eval(name, global_ns, local_ns))
- except Exception as e:
- raise UsageError(f'Could not find module {name}.\n{e.__class__.__name__}: {e}')
-
- profile = LineProfiler(*funcs)
-
- # Get the modules, too
- for modname in opts.m:
- try:
- mod = __import__(modname, fromlist=[''])
- profile.add_module(mod)
- except Exception as e:
- raise UsageError(f'Could not find module {modname}.\n{e.__class__.__name__}: {e}')
-
- if opts.u is not None:
- try:
- output_unit = float(opts.u[0])
- except Exception:
- raise TypeError('Timer unit setting must be a float.')
- else:
- output_unit = None
-
- # Add the profiler to the builtins for @profile.
- import builtins
-
- if 'profile' in builtins.__dict__:
- had_profile = True
- old_profile = builtins.__dict__['profile']
- else:
- had_profile = False
- old_profile = None
- builtins.__dict__['profile'] = profile
-
- try:
- try:
- profile.runctx(arg_str, global_ns, local_ns)
- message = ''
- except SystemExit:
- message = """*** SystemExit exception caught in code being profiled."""
- except KeyboardInterrupt:
- message = ('*** KeyboardInterrupt exception caught in code being '
- 'profiled.')
- finally:
- if had_profile:
- builtins.__dict__['profile'] = old_profile
-
- # Trap text output.
- stdout_trap = StringIO()
- profile.print_stats(stdout_trap, output_unit=output_unit, stripzeros='s' in opts)
- output = stdout_trap.getvalue()
- output = output.rstrip()
-
- page(output)
- print(message, end='')
-
- dump_file = opts.D[0]
- if dump_file:
- profile.dump_stats(dump_file)
- print(f'\n*** Profile stats pickled to file {dump_file!r}. {message}')
-
- text_file = opts.T[0]
- if text_file:
- pfile = open(text_file, 'w')
- pfile.write(output)
- pfile.close()
- print(f'\n*** Profile printout saved to text file {text_file!r}. {message}')
-
- return_value = None
- if 'r' in opts:
- return_value = profile
-
- return return_value
-
-
-def load_ipython_extension(ip):
- """ API for IPython to recognize this module as an IPython extension.
- """
- ip.register_magics(LineProfilerMagics)
-
-
def load_stats(filename):
""" Utility function to load a pickled LineStats object from a given
filename.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/line_profiler.egg-info/PKG-INFO new/line_profiler-3.5.1/line_profiler.egg-info/PKG-INFO
--- old/line_profiler-3.4.0/line_profiler.egg-info/PKG-INFO 1970-01-01 01:00:00.000000000 +0100
+++ new/line_profiler-3.5.1/line_profiler.egg-info/PKG-INFO 2022-05-29 03:05:39.000000000 +0200
@@ -0,0 +1,40 @@
+Metadata-Version: 2.1
+Name: line-profiler
+Version: 3.5.1
+Summary: Line-by-line profiler.
+Home-page: https://github.com/pyutils/line_profiler
+Author: Robert Kern
+Author-email: robert.kern(a)enthought.com
+License: BSD
+Keywords: timing,timer,profiling,profiler,line_profiler
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: C
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Topic :: Software Development
+Description-Content-Type: text/x-rst
+Provides-Extra: all
+Provides-Extra: ipython
+Provides-Extra: tests
+Provides-Extra: build
+License-File: LICENSE.txt
+License-File: LICENSE_Python.txt
+
+line_profiler will profile the time individual lines of code take to execute.
+The profiler is implemented in C via Cython in order to reduce the overhead of
+profiling.
+
+Also included is the script kernprof.py which can be used to conveniently
+profile Python applications and scripts either with line_profiler or with the
+function-level profiling tools in the Python standard library.
+
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/line_profiler.egg-info/SOURCES.txt new/line_profiler-3.5.1/line_profiler.egg-info/SOURCES.txt
--- old/line_profiler-3.4.0/line_profiler.egg-info/SOURCES.txt 1970-01-01 01:00:00.000000000 +0100
+++ new/line_profiler-3.5.1/line_profiler.egg-info/SOURCES.txt 2022-05-29 03:05:39.000000000 +0200
@@ -0,0 +1,42 @@
+CHANGELOG.rst
+CMakeLists.txt
+LICENSE.txt
+LICENSE_Python.txt
+MANIFEST.in
+README.rst
+kernprof.py
+pyproject.toml
+requirements.txt
+run_tests.py
+setup.py
+_skbuild/linux-x86_64-3.8/cmake-install/kernprof.py
+_skbuild/linux-x86_64-3.8/cmake-install/line_profiler/__init__.py
+_skbuild/linux-x86_64-3.8/cmake-install/line_profiler/__main__.py
+_skbuild/linux-x86_64-3.8/cmake-install/line_profiler/_line_profiler.cpython-38-x86_64-linux-gnu.so
+_skbuild/linux-x86_64-3.8/cmake-install/line_profiler/line_profiler.py
+_skbuild/linux-x86_64-3.9/cmake-install/line_profiler/_line_profiler.cpython-39-x86_64-linux-gnu.so
+line_profiler/CMakeLists.txt
+line_profiler/__init__.py
+line_profiler/__main__.py
+line_profiler/_line_profiler.pyx
+line_profiler/ipython_extension.py
+line_profiler/line_profiler.py
+line_profiler/python25.pxd
+line_profiler/timers.c
+line_profiler/timers.h
+line_profiler/unset_trace.c
+line_profiler/unset_trace.h
+line_profiler.egg-info/PKG-INFO
+line_profiler.egg-info/SOURCES.txt
+line_profiler.egg-info/dependency_links.txt
+line_profiler.egg-info/entry_points.txt
+line_profiler.egg-info/requires.txt
+line_profiler.egg-info/top_level.txt
+requirements/build.txt
+requirements/ipython.txt
+requirements/runtime.txt
+requirements/tests.txt
+tests/test_cli.py
+tests/test_ipython.py
+tests/test_kernprof.py
+tests/test_line_profiler.py
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/line_profiler.egg-info/dependency_links.txt new/line_profiler-3.5.1/line_profiler.egg-info/dependency_links.txt
--- old/line_profiler-3.4.0/line_profiler.egg-info/dependency_links.txt 1970-01-01 01:00:00.000000000 +0100
+++ new/line_profiler-3.5.1/line_profiler.egg-info/dependency_links.txt 2022-05-29 03:05:39.000000000 +0200
@@ -0,0 +1 @@
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/line_profiler.egg-info/entry_points.txt new/line_profiler-3.5.1/line_profiler.egg-info/entry_points.txt
--- old/line_profiler-3.4.0/line_profiler.egg-info/entry_points.txt 1970-01-01 01:00:00.000000000 +0100
+++ new/line_profiler-3.5.1/line_profiler.egg-info/entry_points.txt 2022-05-29 03:05:39.000000000 +0200
@@ -0,0 +1,3 @@
+[console_scripts]
+kernprof = kernprof:main
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/line_profiler.egg-info/requires.txt new/line_profiler-3.5.1/line_profiler.egg-info/requires.txt
--- old/line_profiler-3.4.0/line_profiler.egg-info/requires.txt 1970-01-01 01:00:00.000000000 +0100
+++ new/line_profiler-3.5.1/line_profiler.egg-info/requires.txt 2022-05-29 03:05:39.000000000 +0200
@@ -0,0 +1,44 @@
+
+[all]
+Cython
+scikit-build
+cmake
+ninja
+pytest>=4.6.11
+pytest-cov>=2.10.1
+coverage[toml]>=5.3
+ubelt>=1.0.1
+
+[all:python_version <= "3.6"]
+IPython<7.17.0,>=0.13
+IPython<7.17.0,>=0.13
+
+[all:python_version >= "3.7"]
+IPython>=0.13
+IPython>=0.13
+
+[build]
+Cython
+scikit-build
+cmake
+ninja
+
+[ipython]
+
+[ipython:python_version <= "3.6"]
+IPython<7.17.0,>=0.13
+
+[ipython:python_version >= "3.7"]
+IPython>=0.13
+
+[tests]
+pytest>=4.6.11
+pytest-cov>=2.10.1
+coverage[toml]>=5.3
+ubelt>=1.0.1
+
+[tests:python_version <= "3.6"]
+IPython<7.17.0,>=0.13
+
+[tests:python_version >= "3.7"]
+IPython>=0.13
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/line_profiler.egg-info/top_level.txt new/line_profiler-3.5.1/line_profiler.egg-info/top_level.txt
--- old/line_profiler-3.4.0/line_profiler.egg-info/top_level.txt 1970-01-01 01:00:00.000000000 +0100
+++ new/line_profiler-3.5.1/line_profiler.egg-info/top_level.txt 2022-05-29 03:05:39.000000000 +0200
@@ -0,0 +1,2 @@
+kernprof
+line_profiler
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/requirements/ipython.txt new/line_profiler-3.5.1/requirements/ipython.txt
--- old/line_profiler-3.4.0/requirements/ipython.txt 1970-01-01 01:00:00.000000000 +0100
+++ new/line_profiler-3.5.1/requirements/ipython.txt 2022-04-01 08:53:10.000000000 +0200
@@ -0,0 +1,2 @@
+IPython >=0.13 ; python_version >= '3.7'
+IPython >=0.13, <7.17.0 ; python_version <= '3.6'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/requirements/runtime.txt new/line_profiler-3.5.1/requirements/runtime.txt
--- old/line_profiler-3.4.0/requirements/runtime.txt 2021-12-30 02:01:08.000000000 +0100
+++ new/line_profiler-3.5.1/requirements/runtime.txt 2022-04-01 08:53:10.000000000 +0200
@@ -1,2 +0,0 @@
-IPython >=0.13 ; python_version >= '3.7'
-IPython >=0.13, <7.17.0 ; python_version <= '3.6'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/requirements/tests.txt new/line_profiler-3.5.1/requirements/tests.txt
--- old/line_profiler-3.4.0/requirements/tests.txt 2021-12-30 02:01:08.000000000 +0100
+++ new/line_profiler-3.5.1/requirements/tests.txt 2022-04-01 08:54:20.000000000 +0200
@@ -1,4 +1,6 @@
pytest >= 4.6.11
pytest-cov >= 2.10.1
coverage[toml] >= 5.3
-ubelt >= 0.8.7
+ubelt >= 1.0.1
+IPython >=0.13 ; python_version >= '3.7'
+IPython >=0.13, <7.17.0 ; python_version <= '3.6'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/requirements.txt new/line_profiler-3.5.1/requirements.txt
--- old/line_profiler-3.4.0/requirements.txt 2021-12-30 02:01:08.000000000 +0100
+++ new/line_profiler-3.5.1/requirements.txt 2022-04-01 08:53:10.000000000 +0200
@@ -1,3 +1,4 @@
-r requirements/runtime.txt
+-r requirements/ipython.txt
-r requirements/build.txt
-r requirements/tests.txt
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/setup.cfg new/line_profiler-3.5.1/setup.cfg
--- old/line_profiler-3.4.0/setup.cfg 1970-01-01 01:00:00.000000000 +0100
+++ new/line_profiler-3.5.1/setup.cfg 2022-05-29 03:05:39.600498000 +0200
@@ -0,0 +1,4 @@
+[egg_info]
+tag_build =
+tag_date = 0
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/setup.py new/line_profiler-3.5.1/setup.py
--- old/line_profiler-3.4.0/setup.py 2021-12-30 02:01:08.000000000 +0100
+++ new/line_profiler-3.5.1/setup.py 2022-04-01 08:53:10.000000000 +0200
@@ -259,6 +259,7 @@
install_requires=parse_requirements('requirements/runtime.txt'),
extras_require={
'all': parse_requirements('requirements.txt'),
+ 'ipython': parse_requirements('requirements/ipython.txt'),
'tests': parse_requirements('requirements/tests.txt'),
'build': parse_requirements('requirements/build.txt'),
},
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/line_profiler-3.4.0/tests/test_ipython.py new/line_profiler-3.5.1/tests/test_ipython.py
--- old/line_profiler-3.4.0/tests/test_ipython.py 1970-01-01 01:00:00.000000000 +0100
+++ new/line_profiler-3.5.1/tests/test_ipython.py 2022-04-01 08:53:10.000000000 +0200
@@ -0,0 +1,21 @@
+import unittest
+import io
+
+from IPython.testing.globalipapp import get_ipython
+
+class TestIPython(unittest.TestCase):
+ def test_init(self):
+ ip = get_ipython()
+ ip.run_line_magic('load_ext', 'line_profiler')
+ ip.run_cell(raw_cell='def func():\n return 2**20')
+ lprof = ip.run_line_magic('lprun', '-r -f func func()')
+
+ timings = lprof.get_stats().timings
+ self.assertEqual(len(timings), 1) # 1 function
+
+ func_data, lines_data = next(iter(timings.items()))
+ self.assertEqual(func_data[1], 1) # lineno of the function
+ self.assertEqual(func_data[2], "func") # function name
+ self.assertEqual(len(lines_data), 1) # 1 line of code
+ self.assertEqual(lines_data[0][0], 2) # lineno
+ self.assertEqual(lines_data[0][1], 1) # hits
1
0