Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-wurlitzer for
openSUSE:Factory checked in at 2022-04-16 00:14:25
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-wurlitzer (Old)
and /work/SRC/openSUSE:Factory/.python-wurlitzer.new.1941 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-wurlitzer"
Sat Apr 16 00:14:25 2022 rev:6 rq:970225 version:3.0.2
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-wurlitzer/python-wurlitzer.changes
2020-07-24 09:57:40.773510058 +0200
+++
/work/SRC/openSUSE:Factory/.python-wurlitzer.new.1941/python-wurlitzer.changes
2022-04-16 00:14:44.901689087 +0200
@@ -1,0 +2,7 @@
+Thu Apr 14 09:47:12 UTC 2022 - [email protected]
+
+- version update to 3.0.2
+ * no upstream changelog file found
+- python-mock is not required for build
+
+-------------------------------------------------------------------
Old:
----
wurlitzer-2.0.1.tar.gz
New:
----
wurlitzer-3.0.2.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-wurlitzer.spec ++++++
--- /var/tmp/diff_new_pack.UWMJjw/_old 2022-04-16 00:14:45.473689851 +0200
+++ /var/tmp/diff_new_pack.UWMJjw/_new 2022-04-16 00:14:45.477689856 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-wurlitzer
#
-# 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,13 +19,12 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%bcond_without python2
Name: python-wurlitzer
-Version: 2.0.1
+Version: 3.0.2
Release: 0
Summary: Python package to capture C-level output in context managers
License: MIT
URL: https://github.com/minrk/wurlitzer
Source:
https://files.pythonhosted.org/packages/source/w/wurlitzer/wurlitzer-%{version}.tar.gz
-BuildRequires: %{python_module mock}
BuildRequires: %{python_module pytest}
BuildRequires: %{python_module setuptools}
BuildRequires: fdupes
++++++ wurlitzer-2.0.1.tar.gz -> wurlitzer-3.0.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/wurlitzer-2.0.1/.bumpversion.cfg
new/wurlitzer-3.0.2/.bumpversion.cfg
--- old/wurlitzer-2.0.1/.bumpversion.cfg 2020-07-06 10:45:30.000000000
+0200
+++ new/wurlitzer-3.0.2/.bumpversion.cfg 1970-01-01 01:00:00.000000000
+0100
@@ -1,19 +0,0 @@
-[bumpversion]
-current_version = 2.0.1
-parse =
(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\.(?P<release>[a-z0-9]+))?
-tag_name = {new_version}
-allow_dirty = True
-commit = True
-tag = False
-serialize =
- {major}.{minor}.{patch}.{release}
- {major}.{minor}.{patch}
-
-[bumpversion:file:wurlitzer.py]
-
-[bumpversion:part:release]
-optional_value = stable
-values =
- dev
- stable
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/wurlitzer-2.0.1/.gitignore
new/wurlitzer-3.0.2/.gitignore
--- old/wurlitzer-2.0.1/.gitignore 2018-05-20 19:43:32.000000000 +0200
+++ new/wurlitzer-3.0.2/.gitignore 1970-01-01 01:00:00.000000000 +0100
@@ -1,64 +0,0 @@
-# Byte-compiled / optimized / DLL files
-__pycache__/
-*.py[cod]
-*$py.class
-
-# C extensions
-*.so
-
-# Distribution / packaging
-MANIFEST
-.Python
-env/
-build/
-develop-eggs/
-dist/
-downloads/
-eggs/
-.eggs/
-lib/
-lib64/
-parts/
-sdist/
-var/
-*.egg-info/
-.installed.cfg
-*.egg
-
-# PyInstaller
-# Usually these files are written by a python script from a template
-# before PyInstaller builds the exe, so as to inject date/other infos into it.
-*.manifest
-*.spec
-
-# Installer logs
-pip-log.txt
-pip-delete-this-directory.txt
-
-# Unit test / coverage reports
-htmlcov/
-.tox/
-.coverage
-.coverage.*
-.cache
-.pytest_cache
-nosetests.xml
-coverage.xml
-*,cover
-.hypothesis/
-
-# Translations
-*.mo
-*.pot
-
-# Django stuff:
-*.log
-
-# Sphinx documentation
-docs/_build/
-
-# PyBuilder
-target/
-
-#Ipython Notebook
-.ipynb_checkpoints
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/wurlitzer-2.0.1/.travis.yml
new/wurlitzer-3.0.2/.travis.yml
--- old/wurlitzer-2.0.1/.travis.yml 2019-06-13 10:48:40.000000000 +0200
+++ new/wurlitzer-3.0.2/.travis.yml 1970-01-01 01:00:00.000000000 +0100
@@ -1,43 +0,0 @@
-language: python
-python:
- - 2.7
- - 3.4
- - 3.5
- - 3.6
- - nightly
-branches:
- only:
- - master
-before_install:
- - |
- # setup mac virtualenv
- if [[ $(uname) == "Darwin" ]]; then
- brew install python
- python3 -m pip install virtualenv
- virtualenv -p $(which python$PY) ./test-env
- source ./test-env/bin/activate
- fi
-install:
- - pip install --upgrade setuptools pip
- - pip install --upgrade . -r dev-requirements.txt
- - pip freeze
-script:
- - py.test --cov wurlitzer test.py
-after_success:
- - codecov
-env:
- global:
- - HOMEBREW_NO_AUTO_UPDATE=1
-matrix:
- include:
- - os: osx
- language: generic
- env:
- - PY=2
- - os: osx
- language: generic
- env:
- - PY=3
- - os: linux
- dist: xenial
- python: 3.7
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/wurlitzer-2.0.1/CHANGELOG.md
new/wurlitzer-3.0.2/CHANGELOG.md
--- old/wurlitzer-2.0.1/CHANGELOG.md 2020-07-06 10:45:14.000000000 +0200
+++ new/wurlitzer-3.0.2/CHANGELOG.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,83 +0,0 @@
-### Changelog
-
-All notable changes to this project will be documented in this file. Dates are
displayed in UTC.
-
-Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
-
-#### [Unreleased](https://github.com/minrk/wurlitzer/compare/2.0.1...HEAD)
-
-#### [2.0.1](https://github.com/minrk/wurlitzer/compare/2.0.0...2.0.1)
-
-> 6 July 2020
-
-- Merge pull request #38 from minrk/flush-on-exit
[`d24f50c`](https://github.com/minrk/wurlitzer/commit/d24f50c611164a3468622ca2ed80efc3abec8641)
-- flush sys streams on enter/exit
[`6d9b49d`](https://github.com/minrk/wurlitzer/commit/6d9b49dac73e59d5d10a6588ae02ed585779d042)
-- flush sys streams in flush thread as well
[`2682eb4`](https://github.com/minrk/wurlitzer/commit/2682eb4ef34c4268d21ebf75a5f146770a676cbc)
-
-### [2.0.0](https://github.com/minrk/wurlitzer/compare/1.0.3...2.0.0)
-
-> 25 October 2019
-
-- use selectors instead of select.poll
[`#34`](https://github.com/minrk/wurlitzer/pull/34)
-
-#### [1.0.3](https://github.com/minrk/wurlitzer/compare/1.0.2...1.0.3)
-
-> 13 June 2019
-
-- PR: Add thread lock [`#30`](https://github.com/minrk/wurlitzer/pull/30)
-- update packages on travis [`#31`](https://github.com/minrk/wurlitzer/pull/31)
-- test on mac [`#25`](https://github.com/minrk/wurlitzer/pull/25)
-- select.poll timeout is in milliseconds
[`#26`](https://github.com/minrk/wurlitzer/pull/26)
-- using poll instead of select in forwarder
[`#24`](https://github.com/minrk/wurlitzer/pull/24)
-- setup.py improvements [`#19`](https://github.com/minrk/wurlitzer/pull/19)
-- Link blogpost about redirecting stdout/stderr
[`#18`](https://github.com/minrk/wurlitzer/pull/18)
-- fixes #27? [`#27`](https://github.com/minrk/wurlitzer/issues/27)
-
-#### [1.0.2](https://github.com/minrk/wurlitzer/compare/1.0.1...1.0.2)
-
-> 20 May 2018
-
-- move fflush to a thread [`#16`](https://github.com/minrk/wurlitzer/pull/16)
-
-#### [1.0.1](https://github.com/minrk/wurlitzer/compare/1.0.0...1.0.1)
-
-> 22 January 2018
-
-- Test more Pythons [`#13`](https://github.com/minrk/wurlitzer/pull/13)
-- avoid unnecessary close of original FDs
[`#11`](https://github.com/minrk/wurlitzer/pull/11)
-
-### [1.0.0](https://github.com/minrk/wurlitzer/compare/0.2.0...1.0.0)
-
-> 22 June 2017
-
-- use control pipe to signal closure
[`#8`](https://github.com/minrk/wurlitzer/pull/8)
-- import warnings [`#3`](https://github.com/minrk/wurlitzer/pull/3)
-- Do nothing if loaded in terminal IPython
[`#2`](https://github.com/minrk/wurlitzer/pull/2)
-
-#### [0.2.0](https://github.com/minrk/wurlitzer/compare/0.1.2...0.2.0)
-
-> 14 March 2016
-
-- Make it an IPython extension
[`5aa2237`](https://github.com/minrk/wurlitzer/commit/5aa22375de5516915bb1cb9168e04430933e86a6)
-
-#### [0.1.2](https://github.com/minrk/wurlitzer/compare/0.1.1...0.1.2)
-
-> 13 March 2016
-
-- readme more
[`e905543`](https://github.com/minrk/wurlitzer/commit/e9055432933b29a70246299f2534e44af01c7edb)
-- flush before entering wurlitzer
[`a8b3a85`](https://github.com/minrk/wurlitzer/commit/a8b3a856a576fe50e8771fddad7fcf3b21ae3285)
-- bump patch on release
[`841cf92`](https://github.com/minrk/wurlitzer/commit/841cf922a77fd1a954ff968530d096ecfc1879aa)
-
-#### [0.1.1](https://github.com/minrk/wurlitzer/compare/0.1.0...0.1.1)
-
-> 9 March 2016
-
-- fix names in README, long_description
[`7f95a69`](https://github.com/minrk/wurlitzer/commit/7f95a690985e9ff2e7360c2c433fa9b9187f8758)
-
-#### 0.1.0
-
-> 9 March 2016
-
-- init package
[`88e28b7`](https://github.com/minrk/wurlitzer/commit/88e28b7685806006fdd3c9a2021705be1b9fbbed)
-- Add demo notebook
[`ea70e0e`](https://github.com/minrk/wurlitzer/commit/ea70e0e1f82ccb2e3283b6baf2c1d91c0b05ac8a)
-- Initial commit
[`9646cf2`](https://github.com/minrk/wurlitzer/commit/9646cf2417cc46c61d1f6437f8f76efa56ccf2d8)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/wurlitzer-2.0.1/PKG-INFO new/wurlitzer-3.0.2/PKG-INFO
--- old/wurlitzer-2.0.1/PKG-INFO 2020-07-06 10:45:51.000000000 +0200
+++ new/wurlitzer-3.0.2/PKG-INFO 2021-08-25 09:59:24.916883500 +0200
@@ -1,78 +1,80 @@
Metadata-Version: 2.1
Name: wurlitzer
-Version: 2.0.1
+Version: 3.0.2
Summary: Capture C-level output in context managers
Home-page: https://github.com/minrk/wurlitzer
Author: Min RK
Author-email: [email protected]
License: MIT
-Description: # Wurlitzer
-
- Capture C-level stdout/stderr pipes in Python via `os.dup2`.
-
- For more details on why this is needed, please read [this blog post](
https://eli.thegreenplace.net/2015/redirecting-all-kinds-of-stdout-in-python/ ).
-
- ## Install
-
- pip install wurlitzer
-
- ## Usage
-
- Capture stdout/stderr in pipes:
-
- ```python
- from wurlitzer import pipes
-
- with pipes() as (out, err):
- call_some_c_function()
-
- stdout = out.read()
- ```
-
- Capture stdout/stderr in StringIO:
-
- ```python
- from io import StringIO
- from wurlitzer import pipes, STDOUT
-
- out = StringIO()
- with pipes(stdout=out, stderr=STDOUT):
- call_some_c_function()
-
- stdout = out.getvalue()
- ```
-
- Forward C-level stdout/stderr to Python sys.stdout/stderr,
- which may already be forwarded somewhere by the environment, e.g.
IPython:
-
- ```python
- from wurlitzer import sys_pipes
-
- with sys_pipes():
- call_some_c_function()
- ```
-
- Or even simpler, enable it as an IPython extension:
-
- ```
- %load_ext wurlitzer
- ```
-
- To forward all C-level output to IPython during execution.
-
- ## Acknowledgments
-
- This package is based on stuff we learned with @takluyver and @karies
while working on capturing output from the [Cling
Kernel](https://github.com/root-mirror/cling/tree/master/tools/Jupyter/kernel)
for Jupyter.
-
- ## Wurlitzer?!
-
- [Wurlitzer](https://en.wikipedia.org/wiki/Wurlitzer) makes pipe
organs. Get it? Pipes? Naming is hard.
-
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
-Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
-Requires-Python: >=2.7
+Requires-Python: >=3.5
Description-Content-Type: text/markdown
+License-File: LICENSE
+
+# Wurlitzer
+
+Capture C-level stdout/stderr pipes in Python via `os.dup2`.
+
+For more details on why this is needed, please read [this blog
post](https://eli.thegreenplace.net/2015/redirecting-all-kinds-of-stdout-in-python/).
+
+## Install
+
+ pip install wurlitzer
+
+## Usage
+
+Capture stdout/stderr in pipes:
+
+```python
+from wurlitzer import pipes
+
+with pipes() as (out, err):
+ call_some_c_function()
+
+stdout = out.read()
+```
+
+Capture stdout/stderr in StringIO:
+
+```python
+from io import StringIO
+from wurlitzer import pipes, STDOUT
+
+out = StringIO()
+with pipes(stdout=out, stderr=STDOUT):
+ call_some_c_function()
+
+stdout = out.getvalue()
+```
+
+Forward C-level stdout/stderr to Python sys.stdout/stderr,
+which may already be forwarded somewhere by the environment, e.g. IPython:
+
+```python
+from wurlitzer import sys_pipes
+
+with sys_pipes():
+ call_some_c_function()
+```
+
+Or even simpler, enable it as an IPython extension:
+
+```
+%load_ext wurlitzer
+```
+
+To forward all C-level output to IPython during execution.
+
+## Acknowledgments
+
+This package is based on stuff we learned with @takluyver and @karies while
working on capturing output from the [Cling
Kernel](https://github.com/root-mirror/cling/tree/master/tools/Jupyter/kernel)
for Jupyter.
+
+## Wurlitzer?!
+
+[Wurlitzer](https://en.wikipedia.org/wiki/Wurlitzer) makes pipe organs. Get
it? Pipes? Naming is hard.
+
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/wurlitzer-2.0.1/README.md
new/wurlitzer-3.0.2/README.md
--- old/wurlitzer-2.0.1/README.md 2018-12-10 12:49:44.000000000 +0100
+++ new/wurlitzer-3.0.2/README.md 2021-08-25 09:59:17.000000000 +0200
@@ -2,7 +2,7 @@
Capture C-level stdout/stderr pipes in Python via `os.dup2`.
-For more details on why this is needed, please read [this blog post](
https://eli.thegreenplace.net/2015/redirecting-all-kinds-of-stdout-in-python/ ).
+For more details on why this is needed, please read [this blog
post](https://eli.thegreenplace.net/2015/redirecting-all-kinds-of-stdout-in-python/).
## Install
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/wurlitzer-2.0.1/dev-requirements.txt
new/wurlitzer-3.0.2/dev-requirements.txt
--- old/wurlitzer-2.0.1/dev-requirements.txt 2016-03-14 16:07:04.000000000
+0100
+++ new/wurlitzer-3.0.2/dev-requirements.txt 2021-08-25 09:59:17.000000000
+0200
@@ -1,4 +1,4 @@
codecov
-pytest-cov
+mock # python_version < '3.0'
pytest>=2.8
-mock
\ No newline at end of file
+pytest-cov
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/wurlitzer-2.0.1/pyproject.toml
new/wurlitzer-3.0.2/pyproject.toml
--- old/wurlitzer-2.0.1/pyproject.toml 1970-01-01 01:00:00.000000000 +0100
+++ new/wurlitzer-3.0.2/pyproject.toml 2021-08-25 09:59:17.000000000 +0200
@@ -0,0 +1,11 @@
+[tool.isort]
+profile = "black"
+
+[tool.black]
+skip-string-normalization = true
+target_version = [
+ "py27",
+ "py36",
+ "py37",
+ "py38",
+]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/wurlitzer-2.0.1/release.sh
new/wurlitzer-3.0.2/release.sh
--- old/wurlitzer-2.0.1/release.sh 2016-03-09 15:31:50.000000000 +0100
+++ new/wurlitzer-3.0.2/release.sh 1970-01-01 01:00:00.000000000 +0100
@@ -1,9 +0,0 @@
-#!/bin/sh
-set -e
-
-bumpversion release --tag
-py.test test.py
-python setup.py sdist bdist_wheel
-twine upload dist/*
-bumpversion patch
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/wurlitzer-2.0.1/setup.cfg
new/wurlitzer-3.0.2/setup.cfg
--- old/wurlitzer-2.0.1/setup.cfg 2020-07-06 10:45:51.000000000 +0200
+++ new/wurlitzer-3.0.2/setup.cfg 2021-08-25 09:59:24.916883500 +0200
@@ -1,6 +1,3 @@
-[bdist_wheel]
-universal = 1
-
[egg_info]
tag_build =
tag_date = 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/wurlitzer-2.0.1/setup.py new/wurlitzer-3.0.2/setup.py
--- old/wurlitzer-2.0.1/setup.py 2019-10-25 13:41:32.000000000 +0200
+++ new/wurlitzer-3.0.2/setup.py 2021-08-25 09:59:17.000000000 +0200
@@ -33,12 +33,11 @@
author="Min RK",
author_email="[email protected]",
description="Capture C-level output in context managers",
- install_requires=["selectors2; python_version<'3.4'"],
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/minrk/wurlitzer",
py_modules=["wurlitzer"],
- python_requires=">=2.7",
+ python_requires=">=3.5",
license="MIT",
cmdclass={
"bdist_egg": bdist_egg if "bdist_egg" in sys.argv else
bdist_egg_disabled
@@ -47,7 +46,6 @@
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
- "Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
],
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/wurlitzer-2.0.1/test.py new/wurlitzer-3.0.2/test.py
--- old/wurlitzer-2.0.1/test.py 2019-10-17 16:49:50.000000000 +0200
+++ new/wurlitzer-3.0.2/test.py 2021-08-25 09:59:17.000000000 +0200
@@ -3,26 +3,40 @@
import io
import os
-from tempfile import TemporaryFile
+import platform
+import sys
import time
+from fcntl import fcntl
+from tempfile import TemporaryFile
+from unittest import mock
-import mock
+import pytest
+import wurlitzer
from wurlitzer import (
- libc, pipes, STDOUT, PIPE, c_stderr_p, c_stdout_p,
- sys_pipes, sys_pipes_forever,
- stop_sys_pipes,
+ PIPE,
+ STDOUT,
Wurlitzer,
+ c_stderr_p,
+ c_stdout_p,
+ libc,
+ pipes,
+ stop_sys_pipes,
+ sys_pipes,
+ sys_pipes_forever,
)
+
def printf(msg):
"""Call C printf"""
libc.printf((msg + '\n').encode('utf8'))
+
def printf_err(msg):
"""Cal C fprintf on stderr"""
libc.fprintf(c_stderr_p, (msg + '\n').encode('utf8'))
+
def test_pipes():
with pipes(stdout=PIPE, stderr=PIPE) as (stdout, stderr):
printf(u"Hell??")
@@ -31,6 +45,7 @@
assert stdout.read() == u"Hell??\n"
assert stderr.read() == u"Hi, std??rr\n"
+
def test_pipe_bytes():
with pipes(encoding=None) as (stdout, stderr):
printf(u"Hell??")
@@ -39,6 +54,7 @@
assert stdout.read() == u"Hell??\n".encode('utf8')
assert stderr.read() == u"Hi, std??rr\n".encode('utf8')
+
def test_forward():
stdout = io.StringIO()
stderr = io.StringIO()
@@ -51,6 +67,7 @@
assert stdout.getvalue() == u"Hell??\n"
assert stderr.getvalue() == u"Hi, std??rr\n"
+
def test_pipes_stderr():
stdout = io.StringIO()
with pipes(stdout=stdout, stderr=STDOUT) as (_stdout, _stderr):
@@ -63,6 +80,7 @@
assert stdout.getvalue() == u"Hell??\nHi, std??rr\n"
+
def test_flush():
stdout = io.StringIO()
w = Wurlitzer(stdout=stdout, stderr=STDOUT)
@@ -71,16 +89,20 @@
time.sleep(0.5)
assert stdout.getvalue().strip() == u"Hell??"
+
def test_sys_pipes():
stdout = io.StringIO()
stderr = io.StringIO()
- with mock.patch('sys.stdout', stdout), mock.patch('sys.stderr', stderr),
sys_pipes():
+ with mock.patch('sys.stdout', stdout), mock.patch(
+ 'sys.stderr', stderr
+ ), sys_pipes():
printf(u"Hell??")
printf_err(u"Hi, std??rr")
assert stdout.getvalue() == u"Hell??\n"
assert stderr.getvalue() == u"Hi, std??rr\n"
+
def test_redirect_everything():
stdout = io.StringIO()
stderr = io.StringIO()
@@ -118,8 +140,40 @@
def test_buffer_full():
with pipes(stdout=None, stderr=io.StringIO()) as (stdout, stderr):
- long_string = "x" * 1000000 # create a very long string
+ long_string = "x" * 100000 # create a long string (longer than 65536)
printf_err(long_string)
# Test never reaches here as the process hangs.
assert stderr.getvalue() == long_string + "\n"
+
+
+def test_buffer_full_default():
+ with pipes() as (stdout, stderr):
+ long_string = "x" * 100000 # create a long string (longer than 65536)
+ printf(long_string)
+
+ # Test never reaches here as the process hangs.
+ assert stdout.read() == long_string + "\n"
+
+
+def test_pipe_max_size():
+ max_pipe_size = wurlitzer._get_max_pipe_size()
+ if platform.system() == 'Linux':
+ assert 65535 <= max_pipe_size <= 1024 * 1024
+ else:
+ assert max_pipe_size is None
+
+
[email protected](
+ wurlitzer._get_max_pipe_size() is None, reason="requires
_get_max_pipe_size"
+)
+def test_bufsize():
+ default_bufsize = wurlitzer._get_max_pipe_size()
+ with wurlitzer.pipes() as (stdout, stderr):
+ assert fcntl(sys.__stdout__, wurlitzer.F_GETPIPE_SZ) == default_bufsize
+ assert fcntl(sys.__stderr__, wurlitzer.F_GETPIPE_SZ) == default_bufsize
+
+ bufsize = 32768 # seems to only accept powers of two?
+ with wurlitzer.pipes(bufsize=bufsize) as (stdout, stderr):
+ assert fcntl(sys.__stdout__, wurlitzer.F_GETPIPE_SZ) == bufsize
+ assert fcntl(sys.__stderr__, wurlitzer.F_GETPIPE_SZ) == bufsize
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/wurlitzer-2.0.1/wurlitzer.egg-info/PKG-INFO
new/wurlitzer-3.0.2/wurlitzer.egg-info/PKG-INFO
--- old/wurlitzer-2.0.1/wurlitzer.egg-info/PKG-INFO 2020-07-06
10:45:51.000000000 +0200
+++ new/wurlitzer-3.0.2/wurlitzer.egg-info/PKG-INFO 2021-08-25
09:59:24.000000000 +0200
@@ -1,78 +1,80 @@
Metadata-Version: 2.1
Name: wurlitzer
-Version: 2.0.1
+Version: 3.0.2
Summary: Capture C-level output in context managers
Home-page: https://github.com/minrk/wurlitzer
Author: Min RK
Author-email: [email protected]
License: MIT
-Description: # Wurlitzer
-
- Capture C-level stdout/stderr pipes in Python via `os.dup2`.
-
- For more details on why this is needed, please read [this blog post](
https://eli.thegreenplace.net/2015/redirecting-all-kinds-of-stdout-in-python/ ).
-
- ## Install
-
- pip install wurlitzer
-
- ## Usage
-
- Capture stdout/stderr in pipes:
-
- ```python
- from wurlitzer import pipes
-
- with pipes() as (out, err):
- call_some_c_function()
-
- stdout = out.read()
- ```
-
- Capture stdout/stderr in StringIO:
-
- ```python
- from io import StringIO
- from wurlitzer import pipes, STDOUT
-
- out = StringIO()
- with pipes(stdout=out, stderr=STDOUT):
- call_some_c_function()
-
- stdout = out.getvalue()
- ```
-
- Forward C-level stdout/stderr to Python sys.stdout/stderr,
- which may already be forwarded somewhere by the environment, e.g.
IPython:
-
- ```python
- from wurlitzer import sys_pipes
-
- with sys_pipes():
- call_some_c_function()
- ```
-
- Or even simpler, enable it as an IPython extension:
-
- ```
- %load_ext wurlitzer
- ```
-
- To forward all C-level output to IPython during execution.
-
- ## Acknowledgments
-
- This package is based on stuff we learned with @takluyver and @karies
while working on capturing output from the [Cling
Kernel](https://github.com/root-mirror/cling/tree/master/tools/Jupyter/kernel)
for Jupyter.
-
- ## Wurlitzer?!
-
- [Wurlitzer](https://en.wikipedia.org/wiki/Wurlitzer) makes pipe
organs. Get it? Pipes? Naming is hard.
-
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
-Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
-Requires-Python: >=2.7
+Requires-Python: >=3.5
Description-Content-Type: text/markdown
+License-File: LICENSE
+
+# Wurlitzer
+
+Capture C-level stdout/stderr pipes in Python via `os.dup2`.
+
+For more details on why this is needed, please read [this blog
post](https://eli.thegreenplace.net/2015/redirecting-all-kinds-of-stdout-in-python/).
+
+## Install
+
+ pip install wurlitzer
+
+## Usage
+
+Capture stdout/stderr in pipes:
+
+```python
+from wurlitzer import pipes
+
+with pipes() as (out, err):
+ call_some_c_function()
+
+stdout = out.read()
+```
+
+Capture stdout/stderr in StringIO:
+
+```python
+from io import StringIO
+from wurlitzer import pipes, STDOUT
+
+out = StringIO()
+with pipes(stdout=out, stderr=STDOUT):
+ call_some_c_function()
+
+stdout = out.getvalue()
+```
+
+Forward C-level stdout/stderr to Python sys.stdout/stderr,
+which may already be forwarded somewhere by the environment, e.g. IPython:
+
+```python
+from wurlitzer import sys_pipes
+
+with sys_pipes():
+ call_some_c_function()
+```
+
+Or even simpler, enable it as an IPython extension:
+
+```
+%load_ext wurlitzer
+```
+
+To forward all C-level output to IPython during execution.
+
+## Acknowledgments
+
+This package is based on stuff we learned with @takluyver and @karies while
working on capturing output from the [Cling
Kernel](https://github.com/root-mirror/cling/tree/master/tools/Jupyter/kernel)
for Jupyter.
+
+## Wurlitzer?!
+
+[Wurlitzer](https://en.wikipedia.org/wiki/Wurlitzer) makes pipe organs. Get
it? Pipes? Naming is hard.
+
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/wurlitzer-2.0.1/wurlitzer.egg-info/SOURCES.txt
new/wurlitzer-3.0.2/wurlitzer.egg-info/SOURCES.txt
--- old/wurlitzer-2.0.1/wurlitzer.egg-info/SOURCES.txt 2020-07-06
10:45:51.000000000 +0200
+++ new/wurlitzer-3.0.2/wurlitzer.egg-info/SOURCES.txt 2021-08-25
09:59:24.000000000 +0200
@@ -1,19 +1,13 @@
-.bumpversion.cfg
-.gitignore
-.travis.yml
-CHANGELOG.md
Demo.ipynb
LICENSE
MANIFEST.in
README.md
dev-requirements.txt
-release.sh
-setup.cfg
+pyproject.toml
setup.py
test.py
wurlitzer.py
wurlitzer.egg-info/PKG-INFO
wurlitzer.egg-info/SOURCES.txt
wurlitzer.egg-info/dependency_links.txt
-wurlitzer.egg-info/requires.txt
wurlitzer.egg-info/top_level.txt
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/wurlitzer-2.0.1/wurlitzer.egg-info/requires.txt
new/wurlitzer-3.0.2/wurlitzer.egg-info/requires.txt
--- old/wurlitzer-2.0.1/wurlitzer.egg-info/requires.txt 2020-07-06
10:45:51.000000000 +0200
+++ new/wurlitzer-3.0.2/wurlitzer.egg-info/requires.txt 1970-01-01
01:00:00.000000000 +0100
@@ -1,3 +0,0 @@
-
-[:python_version < "3.4"]
-selectors2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/wurlitzer-2.0.1/wurlitzer.py
new/wurlitzer-3.0.2/wurlitzer.py
--- old/wurlitzer-2.0.1/wurlitzer.py 2020-07-06 10:45:30.000000000 +0200
+++ new/wurlitzer-3.0.2/wurlitzer.py 2021-08-25 09:59:17.000000000 +0200
@@ -4,7 +4,7 @@
"""
from __future__ import print_function
-__version__ = '2.0.1'
+__version__ = '3.0.2'
__all__ = [
'pipes',
@@ -14,38 +14,81 @@
'Wurlitzer',
]
-from contextlib import contextmanager
import ctypes
import errno
-from fcntl import fcntl, F_GETFL, F_SETFL
import io
import os
-
-try:
- from queue import Queue
-except ImportError:
- from Queue import Queue
-
-try:
- import selectors
-except ImportError:
- # py < 3.4
- import selectors2 as selectors
-
+import platform
+import selectors
import sys
import threading
import time
import warnings
+from contextlib import contextmanager
+from fcntl import F_GETFL, F_SETFL, fcntl
+from functools import lru_cache
+from queue import Queue
+
+try:
+ from fcntl import F_GETPIPE_SZ, F_SETPIPE_SZ
+except ImportError:
+ # ref: linux uapi/linux/fcntl.h
+ F_SETPIPE_SZ = 1024 + 7
+ F_GETPIPE_SZ = 1024 + 8
libc = ctypes.CDLL(None)
+
+def _get_streams_cffi():
+ """Use CFFI to lookup stdout/stderr pointers
+
+ Should work ~everywhere, but requires compilation
+ """
+ try:
+ import cffi
+ except ImportError:
+ raise ImportError(
+ "Failed to lookup stdout symbols in libc. Fallback requires cffi."
+ )
+
+ try:
+ _ffi = cffi.FFI()
+ _ffi.cdef("const size_t c_stdout_p();")
+ _ffi.cdef("const size_t c_stderr_p();")
+ _lib = _ffi.verify(
+ '\n'.join(
+ [
+ "#include <stdio.h>",
+ "const size_t c_stdout_p() { return (size_t) (void*)
stdout; }",
+ "const size_t c_stderr_p() { return (size_t) (void*)
stderr; }",
+ ]
+ )
+ )
+ c_stdout_p = ctypes.c_void_p(_lib.c_stdout_p())
+ c_stderr_p = ctypes.c_void_p(_lib.c_stderr_p())
+ except Exception as e:
+ warnings.warn(
+ "Failed to lookup stdout with cffi: {}.\nStreams may not be
flushed.".format(
+ e
+ )
+ )
+ return (None, None)
+ else:
+ return c_stdout_p, c_stderr_p
+
+
+c_stdout_p = c_stderr_p = None
try:
c_stdout_p = ctypes.c_void_p.in_dll(libc, 'stdout')
c_stderr_p = ctypes.c_void_p.in_dll(libc, 'stderr')
-except ValueError: # pragma: no cover
- # libc.stdout is has a funny name on OS X
- c_stdout_p = ctypes.c_void_p.in_dll(libc, '__stdoutp') # pragma: no cover
- c_stderr_p = ctypes.c_void_p.in_dll(libc, '__stderrp') # pragma: no cover
+except ValueError:
+ # libc.stdout has a funny name on macOS
+ try:
+ c_stdout_p = ctypes.c_void_p.in_dll(libc, '__stdoutp')
+ c_stderr_p = ctypes.c_void_p.in_dll(libc, '__stderrp')
+ except ValueError:
+ c_stdout_p, c_stderr_p = _get_streams_cffi()
+
STDOUT = 2
PIPE = 3
@@ -53,7 +96,7 @@
_default_encoding = getattr(sys.stdin, 'encoding', None) or 'utf8'
if _default_encoding.lower() == 'ascii':
# don't respect ascii
- _default_encoding = 'utf8' # pragma: no cover
+ _default_encoding = 'utf8' # pragma: no cover
def dup2(a, b, timeout=3):
@@ -73,14 +116,56 @@
raise dup_err
-class Wurlitzer(object):
+@lru_cache()
+def _get_max_pipe_size():
+ """Get max pipe size
+
+ Reads /proc/sys/fs/pipe-max-size on Linux.
+ Always returns None elsewhere.
+
+ Returns integer (up to 1MB),
+ or None if no value can be determined.
+
+ Adapted from wal-e, (c) 2018, WAL-E Contributors
+ used under BSD-3-clause
+ """
+ if platform.system() != 'Linux':
+ return
+
+ # If Linux procfs (or something that looks like it) exposes its
+ # maximum F_SETPIPE_SZ, adjust the default buffer sizes.
+ try:
+ with open('/proc/sys/fs/pipe-max-size', 'r') as f:
+ # Figure out OS max pipe size
+ pipe_max_size = int(f.read())
+ except Exception:
+ pass
+ else:
+ if pipe_max_size > 1024 * 1024:
+ # avoid unusually large values, limit to 1MB
+ return 1024 * 1024
+ elif pipe_max_size <= 65536:
+ # smaller than default, don't do anything
+ return None
+ else:
+ return pipe_max_size
+
+
+class Wurlitzer:
"""Class for Capturing Process-level FD output via dup2
-
- Typically used via `wurlitzer.capture`
+
+ Typically used via `wurlitzer.pipes`
"""
+
flush_interval = 0.2
-
- def __init__(self, stdout=None, stderr=None, encoding=_default_encoding):
+
+ def __init__(
+ self,
+ stdout=None,
+ stderr=None,
+ encoding=_default_encoding,
+ bufsize=_get_max_pipe_size(),
+ ):
"""
Parameters
----------
@@ -90,6 +175,10 @@
The stream for forwarding stderr.
encoding: str or None
The encoding to use, if streams should be interpreted as text.
+ bufsize: int or None
+ Set pipe buffer size using fcntl F_SETPIPE_SZ (linux only)
+ default: use /proc/sys/fs/pipe-max-size up to a max of 1MB
+ if 0, will do nothing.
"""
self._stdout = stdout
if stderr == STDOUT:
@@ -97,6 +186,9 @@
else:
self._stderr = stderr
self.encoding = encoding
+ if bufsize is None:
+ bufsize = _get_max_pipe_size()
+ self._bufsize = bufsize
self._save_fds = {}
self._real_fds = {}
self._handlers = {}
@@ -107,34 +199,41 @@
real_fd = getattr(sys, '__%s__' % name).fileno()
save_fd = os.dup(real_fd)
self._save_fds[name] = save_fd
-
+
pipe_out, pipe_in = os.pipe()
+ # set max pipe buffer size (linux only)
+ if self._bufsize:
+ try:
+ fcntl(pipe_in, F_SETPIPE_SZ, self._bufsize)
+ except OSError:
+ warnings.warn("Failed to set pipe buffer size", RuntimeWarning)
+
dup2(pipe_in, real_fd)
os.close(pipe_in)
self._real_fds[name] = real_fd
-
+
# make pipe_out non-blocking
flags = fcntl(pipe_out, F_GETFL)
- fcntl(pipe_out, F_SETFL, flags|os.O_NONBLOCK)
+ fcntl(pipe_out, F_SETFL, flags | os.O_NONBLOCK)
return pipe_out
-
+
def _decode(self, data):
"""Decode data, if any
-
+
Called before passing to stdout/stderr streams
"""
if self.encoding:
data = data.decode(self.encoding, 'replace')
return data
-
+
def _handle_stdout(self, data):
if self._stdout:
self._stdout.write(self._decode(data))
-
+
def _handle_stderr(self, data):
if self._stderr:
self._stderr.write(self._decode(data))
-
+
def _setup_handle(self):
"""Setup handle for output, if any"""
self.handle = (self._stdout, self._stderr)
@@ -150,8 +249,11 @@
if self._stderr and sys.stderr:
sys.stderr.flush()
- libc.fflush(c_stdout_p)
- libc.fflush(c_stderr_p)
+ if c_stdout_p is not None:
+ libc.fflush(c_stdout_p)
+
+ if c_stderr_p is not None:
+ libc.fflush(c_stderr_p)
def __enter__(self):
# flush anything out before starting
@@ -241,6 +343,7 @@
flush_thread.join()
# cleanup pipes
[os.close(pipe) for pipe in pipes]
+ poller.close()
self.thread = threading.Thread(target=forwarder)
self.thread.daemon = True
@@ -267,11 +370,19 @@
@contextmanager
-def pipes(stdout=PIPE, stderr=PIPE, encoding=_default_encoding):
+def pipes(stdout=PIPE, stderr=PIPE, encoding=_default_encoding, bufsize=None):
"""Capture C-level stdout/stderr in a context manager.
The return value for the context manager is (stdout, stderr).
+ .. versionchanged:: 3.0
+
+ when using `PIPE` (default), the type of captured output
+ is `io.StringIO/BytesIO` instead of an OS pipe.
+ This eliminates max buffer size issues (and hang when output exceeds
65536 bytes),
+ but also means the buffer cannot be read with `.read()` methods
+ until after the context exits.
+
Examples
--------
@@ -280,14 +391,13 @@
... output = stdout.read()
"""
stdout_pipe = stderr_pipe = False
+ if encoding:
+ PipeIO = io.StringIO
+ else:
+ PipeIO = io.BytesIO
# setup stdout
if stdout == PIPE:
- stdout_r, stdout_w = os.pipe()
- stdout_w = os.fdopen(stdout_w, 'wb')
- if encoding:
- stdout_r = io.open(stdout_r, 'r', encoding=encoding)
- else:
- stdout_r = os.fdopen(stdout_r, 'rb')
+ stdout_r = stdout_w = PipeIO()
stdout_pipe = True
else:
stdout_r = stdout_w = stdout
@@ -296,46 +406,39 @@
stderr_r = None
stderr_w = stdout_w
elif stderr == PIPE:
- stderr_r, stderr_w = os.pipe()
- stderr_w = os.fdopen(stderr_w, 'wb')
- if encoding:
- stderr_r = io.open(stderr_r, 'r', encoding=encoding)
- else:
- stderr_r = os.fdopen(stderr_r, 'rb')
+ stderr_r = stderr_w = PipeIO()
stderr_pipe = True
else:
stderr_r = stderr_w = stderr
- if stdout_pipe or stderr_pipe:
- capture_encoding = None
- else:
- capture_encoding = encoding
- w = Wurlitzer(stdout=stdout_w, stderr=stderr_w, encoding=capture_encoding)
+ w = Wurlitzer(stdout=stdout_w, stderr=stderr_w, encoding=encoding,
bufsize=bufsize)
try:
with w:
yield stdout_r, stderr_r
finally:
# close pipes
if stdout_pipe:
- stdout_w.close()
+ # seek to 0 so that it can be read after exit
+ stdout_r.seek(0)
if stderr_pipe:
- stderr_w.close()
+ # seek to 0 so that it can be read after exit
+ stderr_r.seek(0)
-def sys_pipes(encoding=_default_encoding):
+def sys_pipes(encoding=_default_encoding, bufsize=None):
"""Redirect C-level stdout/stderr to sys.stdout/stderr
-
+
This is useful of sys.sdout/stderr are already being forwarded somewhere.
-
+
DO NOT USE THIS if sys.stdout and sys.stderr are not already being
forwarded.
"""
- return pipes(sys.stdout, sys.stderr, encoding=encoding)
+ return pipes(sys.stdout, sys.stderr, encoding=encoding, bufsize=bufsize)
_mighty_wurlitzer = None
_mighty_lock = threading.Lock()
-def sys_pipes_forever(encoding=_default_encoding):
+def sys_pipes_forever(encoding=_default_encoding, bufsize=None):
"""Redirect all C output to sys.stdout/err
This is not a context manager; it turns on C-forwarding permanently.
@@ -343,7 +446,7 @@
global _mighty_wurlitzer
with _mighty_lock:
if _mighty_wurlitzer is None:
- _mighty_wurlitzer = sys_pipes(encoding)
+ _mighty_wurlitzer = sys_pipes(encoding, bufsize)
_mighty_wurlitzer.__enter__()
@@ -356,30 +459,45 @@
_mighty_wurlitzer = None
+_extension_enabled = False
+
+
def load_ipython_extension(ip):
"""Register me as an IPython extension
-
+
Captures all C output during execution and forwards to sys.
-
+
Does nothing on terminal IPython.
-
+
Use: %load_ext wurlitzer
"""
- if not getattr(ip, 'kernel'):
- warnings.warn(
- "wurlitzer extension doesn't do anything in terminal IPython"
- )
+ global _extension_enabled
+
+ if not getattr(ip, 'kernel', None):
+ warnings.warn("wurlitzer extension doesn't do anything in terminal
IPython")
return
+ for name in ("__stdout__", "__stderr__"):
+ if getattr(sys, name) is None:
+ warnings.warn("sys.{} is None. Wurlitzer can't capture output
without it.")
+ return
+
ip.events.register('pre_execute', sys_pipes_forever)
ip.events.register('post_execute', stop_sys_pipes)
+ _extension_enabled = True
def unload_ipython_extension(ip):
"""Unload me as an IPython extension
-
+
Use: %unload_ext wurlitzer
"""
- if not getattr(ip, 'kernel'):
+ global _extension_enabled
+ if not _extension_enabled:
return
+
ip.events.unregister('pre_execute', sys_pipes_forever)
ip.events.unregister('post_execute', stop_sys_pipes)
+ # sys_pipes_forever was called in pre_execute
+ # after unregister we need to call it explicitly:
+ stop_sys_pipes()
+ _extension_enabled = False