Hello community,
here is the log from the commit of package python-python-redmine for
openSUSE:Factory checked in at 2020-08-06 10:42:07
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-python-redmine (Old)
and /work/SRC/openSUSE:Factory/.python-python-redmine.new.3399 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-python-redmine"
Thu Aug 6 10:42:07 2020 rev:3 rq:824578 version:2.3.0
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-python-redmine/python-python-redmine.changes
2020-04-22 20:51:37.523231665 +0200
+++
/work/SRC/openSUSE:Factory/.python-python-redmine.new.3399/python-python-redmine.changes
2020-08-06 10:42:16.102124492 +0200
@@ -1,0 +2,21 @@
+Wed Aug 5 09:33:59 UTC 2020 - Marketa Calabkova <[email protected]>
+
+- Update to version 2.3.0
+ * Support custom filename in ``redmine.upload()``
+ * Support for ``get()`` and ``update()`` operations for ``/my/account``
endpoint which doesn't require admin
+ privileges by using ``me`` as an id, i.e. ``redmine.user.get('me')`` or
``redmine.user.update('me',firstname='John')``
+ (requires Redmine >= 4.1.0)
+ * News ``create()``, ``update()``, ``delete()`` operations support (requires
Redmine >= 4.1.0)
+ * ResourceSet's ``export()`` method now supports ``columns`` keyword
argument which can be either an iterable
+ of column names, an "all" string which tells Python*Redmine to export all
available columns, "all_gui" string
+ for GUI like behaviour or iterable of elements with "all_gui" string and
additional columns to export
+ * Added support for special characters in WikiPage titles (`Issue #222
<https://github.com/maxtepkeev/python-redmine/
+ issues/222>`__) (thanks to `Radek Czajka <https://github.com/rczajka>`__)
+ * Added ``return_response`` and ``ignore_response`` parameters to engine
which allow to skip response processing
+ and speed up the create/update/delete operation in case response body
isn't needed (see
+ `docs
<https://python*redmine.com/advanced/request_engines.html#session>`__ for
details)
+ * *Backwards Incompatible:* Requests version required >= 2.23.0
+ * *Backwards Incompatible:* Removed Python 3.4 support as it's not supported
by Requests anymore
+- Drop apparently obsolete python-python-redmine-use-system-requests.patch
+
+-------------------------------------------------------------------
Old:
----
python-python-redmine-use-system-requests.patch
python-redmine-2.2.1.tar.gz
New:
----
python-redmine-2.3.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-python-redmine.spec ++++++
--- /var/tmp/diff_new_pack.EmGb2m/_old 2020-08-06 10:42:16.766124824 +0200
+++ /var/tmp/diff_new_pack.EmGb2m/_new 2020-08-06 10:42:16.766124824 +0200
@@ -19,21 +19,20 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%bcond_without python2
Name: python-python-redmine
-Version: 2.2.1
+Version: 2.3.0
Release: 0
Summary: Python library for the Redmine RESTful API
License: Apache-2.0
Group: Development/Languages/Python
URL: https://python-redmine.com
Source:
https://files.pythonhosted.org/packages/source/p/python-redmine/python-redmine-%{version}.tar.gz
-Patch0: python-python-redmine-use-system-requests.patch
BuildRequires: %{python_module coverage}
BuildRequires: %{python_module nose}
-BuildRequires: %{python_module requests >= 2.20.0}
+BuildRequires: %{python_module requests >= 2.23.0}
BuildRequires: %{python_module setuptools}
BuildRequires: fdupes
BuildRequires: python-rpm-macros
-Requires: python-requests >= 2.20.0
+Requires: python-requests >= 2.23.0
BuildArch: noarch
%if %{with python2}
BuildRequires: python-mock
@@ -48,7 +47,6 @@
%prep
%setup -q -n python-redmine-%{version}
-%patch0 -p1
%build
%python_build
++++++ python-redmine-2.2.1.tar.gz -> python-redmine-2.3.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-redmine-2.2.1/CHANGELOG.rst
new/python-redmine-2.3.0/CHANGELOG.rst
--- old/python-redmine-2.2.1/CHANGELOG.rst 2019-02-28 12:40:05.000000000
+0100
+++ new/python-redmine-2.3.0/CHANGELOG.rst 2020-05-21 13:45:19.000000000
+0200
@@ -1,6 +1,45 @@
Changelog
---------
+2.3.0 (2020-05-21)
+++++++++++++++++++
+
+**Improvements**:
+
+- Support custom filename in ``redmine.upload()``
+- Support for ``get()`` and ``update()`` operations for ``/my/account``
endpoint which doesn't require admin
+ privileges by using ``me`` as an id, i.e. ``redmine.user.get('me')`` or
``redmine.user.update('me',firstname='John')``
+ (requires Redmine >= 4.1.0)
+- News ``create()``, ``update()``, ``delete()`` operations support (requires
Redmine >= 4.1.0)
+- ResourceSet's ``export()`` method now supports ``columns`` keyword argument
which can be either an iterable
+ of column names, an "all" string which tells Python-Redmine to export all
available columns, "all_gui" string
+ for GUI like behaviour or iterable of elements with "all_gui" string and
additional columns to export
+- Added support for special characters in WikiPage titles (`Issue #222
<https://github.com/maxtepkeev/python-redmine/
+ issues/222>`__) (thanks to `Radek Czajka <https://github.com/rczajka>`__)
+- Added ``return_response`` and ``ignore_response`` parameters to engine which
allow to skip response processing
+ and speed up the create/update/delete operation in case response body isn't
needed (see
+ `docs <https://python-redmine.com/advanced/request_engines.html#session>`__
for details)
+
+**Changes**:
+
+- *Backwards Incompatible:* Requests version required >= 2.23.0
+- *Backwards Incompatible:* Removed Python 3.4 support as it's not supported
by Requests anymore
+
+**Bugfixes**:
+
+- User's ``send_information`` field wasn't sent correctly to Redmine so
account information emails were
+ never sent (`Issue #227
<https://github.com/maxtepkeev/python-redmine/pull/227>`__) (thanks to
+ `wodny <https://github.com/wodny>`__)
+- Project resource ``default_version`` attribute was returned as a dict
instead of being converted to
+ Resource object
+- Resource object was leaking memory during initialization (`Issue #257
<https://github.com/maxtepkeev/python-redmine/
+ issues/257>`__) (thanks to `yihli <https://github.com/qianyi3210>`__)
+
+**Documentation**:
+
+- Introduced detailed parameter list for ``redmine.session``
+- Mentioned support for ``admin`` in User's resource create/update
+
2.2.1 (2019-02-28)
++++++++++++++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-redmine-2.2.1/LICENSE
new/python-redmine-2.3.0/LICENSE
--- old/python-redmine-2.2.1/LICENSE 2019-01-13 11:14:36.000000000 +0100
+++ new/python-redmine-2.3.0/LICENSE 2020-05-20 14:31:32.000000000 +0200
@@ -1,4 +1,4 @@
-Copyright 2019 Maxim Tepkeev
+Copyright 2020 Maxim Tepkeev
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-redmine-2.2.1/NOTICE
new/python-redmine-2.3.0/NOTICE
--- old/python-redmine-2.2.1/NOTICE 2019-01-12 11:45:54.000000000 +0100
+++ new/python-redmine-2.3.0/NOTICE 2020-05-07 11:14:49.000000000 +0200
@@ -3,7 +3,7 @@
Six License
===========
-Copyright (c) 2010-2019 Benjamin Peterson
+Copyright (c) 2010-2020 Benjamin Peterson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-redmine-2.2.1/PKG-INFO
new/python-redmine-2.3.0/PKG-INFO
--- old/python-redmine-2.2.1/PKG-INFO 2019-02-28 12:42:23.000000000 +0100
+++ new/python-redmine-2.3.0/PKG-INFO 2020-05-21 13:48:11.953657900 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 1.2
Name: python-redmine
-Version: 2.2.1
+Version: 2.3.0
Summary: Library for communicating with a Redmine project management
application
Home-page: https://github.com/maxtepkeev/python-redmine
Author: Maxim Tepkeev
@@ -63,7 +63,7 @@
* Supports 100% of Redmine API
* Supports external Redmine plugins API
- * Supports Python 2.7, 3.4 - 3.7, PyPy and PyPy3
+ * Supports Python 2.7, 3.5 - 3.8, PyPy and PyPy3
* Supports different request engines
* Extendable via custom resources and custom request engines
* Extensively documented
@@ -112,6 +112,45 @@
Changelog
---------
+ 2.3.0 (2020-05-21)
+ ++++++++++++++++++
+
+ **Improvements**:
+
+ - Support custom filename in ``redmine.upload()``
+ - Support for ``get()`` and ``update()`` operations for
``/my/account`` endpoint which doesn't require admin
+ privileges by using ``me`` as an id, i.e. ``redmine.user.get('me')``
or ``redmine.user.update('me',firstname='John')``
+ (requires Redmine >= 4.1.0)
+ - News ``create()``, ``update()``, ``delete()`` operations support
(requires Redmine >= 4.1.0)
+ - ResourceSet's ``export()`` method now supports ``columns`` keyword
argument which can be either an iterable
+ of column names, an "all" string which tells Python-Redmine to
export all available columns, "all_gui" string
+ for GUI like behaviour or iterable of elements with "all_gui" string
and additional columns to export
+ - Added support for special characters in WikiPage titles (`Issue #222
<https://github.com/maxtepkeev/python-redmine/
+ issues/222>`__) (thanks to `Radek Czajka
<https://github.com/rczajka>`__)
+ - Added ``return_response`` and ``ignore_response`` parameters to
engine which allow to skip response processing
+ and speed up the create/update/delete operation in case response
body isn't needed (see
+ `docs
<https://python-redmine.com/advanced/request_engines.html#session>`__ for
details)
+
+ **Changes**:
+
+ - *Backwards Incompatible:* Requests version required >= 2.23.0
+ - *Backwards Incompatible:* Removed Python 3.4 support as it's not
supported by Requests anymore
+
+ **Bugfixes**:
+
+ - User's ``send_information`` field wasn't sent correctly to Redmine
so account information emails were
+ never sent (`Issue #227
<https://github.com/maxtepkeev/python-redmine/pull/227>`__) (thanks to
+ `wodny <https://github.com/wodny>`__)
+ - Project resource ``default_version`` attribute was returned as a
dict instead of being converted to
+ Resource object
+ - Resource object was leaking memory during initialization (`Issue
#257 <https://github.com/maxtepkeev/python-redmine/
+ issues/257>`__) (thanks to `yihli <https://github.com/qianyi3210>`__)
+
+ **Documentation**:
+
+ - Introduced detailed parameter list for ``redmine.session``
+ - Mentioned support for ``admin`` in User's resource create/update
+
2.2.1 (2019-02-28)
++++++++++++++++++
@@ -772,10 +811,10 @@
Classifier: Environment :: Web Environment
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
-Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-redmine-2.2.1/README.rst
new/python-redmine-2.3.0/README.rst
--- old/python-redmine-2.2.1/README.rst 2019-01-04 11:44:32.000000000 +0100
+++ new/python-redmine-2.3.0/README.rst 2020-05-07 12:26:23.000000000 +0200
@@ -54,7 +54,7 @@
* Supports 100% of Redmine API
* Supports external Redmine plugins API
-* Supports Python 2.7, 3.4 - 3.7, PyPy and PyPy3
+* Supports Python 2.7, 3.5 - 3.8, PyPy and PyPy3
* Supports different request engines
* Extendable via custom resources and custom request engines
* Extensively documented
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-redmine-2.2.1/python_redmine.egg-info/PKG-INFO
new/python-redmine-2.3.0/python_redmine.egg-info/PKG-INFO
--- old/python-redmine-2.2.1/python_redmine.egg-info/PKG-INFO 2019-02-28
12:42:23.000000000 +0100
+++ new/python-redmine-2.3.0/python_redmine.egg-info/PKG-INFO 2020-05-21
13:48:11.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 1.2
Name: python-redmine
-Version: 2.2.1
+Version: 2.3.0
Summary: Library for communicating with a Redmine project management
application
Home-page: https://github.com/maxtepkeev/python-redmine
Author: Maxim Tepkeev
@@ -63,7 +63,7 @@
* Supports 100% of Redmine API
* Supports external Redmine plugins API
- * Supports Python 2.7, 3.4 - 3.7, PyPy and PyPy3
+ * Supports Python 2.7, 3.5 - 3.8, PyPy and PyPy3
* Supports different request engines
* Extendable via custom resources and custom request engines
* Extensively documented
@@ -112,6 +112,45 @@
Changelog
---------
+ 2.3.0 (2020-05-21)
+ ++++++++++++++++++
+
+ **Improvements**:
+
+ - Support custom filename in ``redmine.upload()``
+ - Support for ``get()`` and ``update()`` operations for
``/my/account`` endpoint which doesn't require admin
+ privileges by using ``me`` as an id, i.e. ``redmine.user.get('me')``
or ``redmine.user.update('me',firstname='John')``
+ (requires Redmine >= 4.1.0)
+ - News ``create()``, ``update()``, ``delete()`` operations support
(requires Redmine >= 4.1.0)
+ - ResourceSet's ``export()`` method now supports ``columns`` keyword
argument which can be either an iterable
+ of column names, an "all" string which tells Python-Redmine to
export all available columns, "all_gui" string
+ for GUI like behaviour or iterable of elements with "all_gui" string
and additional columns to export
+ - Added support for special characters in WikiPage titles (`Issue #222
<https://github.com/maxtepkeev/python-redmine/
+ issues/222>`__) (thanks to `Radek Czajka
<https://github.com/rczajka>`__)
+ - Added ``return_response`` and ``ignore_response`` parameters to
engine which allow to skip response processing
+ and speed up the create/update/delete operation in case response
body isn't needed (see
+ `docs
<https://python-redmine.com/advanced/request_engines.html#session>`__ for
details)
+
+ **Changes**:
+
+ - *Backwards Incompatible:* Requests version required >= 2.23.0
+ - *Backwards Incompatible:* Removed Python 3.4 support as it's not
supported by Requests anymore
+
+ **Bugfixes**:
+
+ - User's ``send_information`` field wasn't sent correctly to Redmine
so account information emails were
+ never sent (`Issue #227
<https://github.com/maxtepkeev/python-redmine/pull/227>`__) (thanks to
+ `wodny <https://github.com/wodny>`__)
+ - Project resource ``default_version`` attribute was returned as a
dict instead of being converted to
+ Resource object
+ - Resource object was leaking memory during initialization (`Issue
#257 <https://github.com/maxtepkeev/python-redmine/
+ issues/257>`__) (thanks to `yihli <https://github.com/qianyi3210>`__)
+
+ **Documentation**:
+
+ - Introduced detailed parameter list for ``redmine.session``
+ - Mentioned support for ``admin`` in User's resource create/update
+
2.2.1 (2019-02-28)
++++++++++++++++++
@@ -772,10 +811,10 @@
Classifier: Environment :: Web Environment
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
-Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-redmine-2.2.1/python_redmine.egg-info/requires.txt
new/python-redmine-2.3.0/python_redmine.egg-info/requires.txt
--- old/python-redmine-2.2.1/python_redmine.egg-info/requires.txt
2019-02-28 12:42:23.000000000 +0100
+++ new/python-redmine-2.3.0/python_redmine.egg-info/requires.txt
2020-05-21 13:48:11.000000000 +0200
@@ -1 +1 @@
-requests>=2.20.0
+requests>=2.23.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-redmine-2.2.1/redminelib/__init__.py
new/python-redmine-2.3.0/redminelib/__init__.py
--- old/python-redmine-2.2.1/redminelib/__init__.py 2019-01-03
08:27:49.000000000 +0100
+++ new/python-redmine-2.3.0/redminelib/__init__.py 2020-05-21
13:25:05.000000000 +0200
@@ -33,15 +33,14 @@
:param raise_attr_exception: (optional). Control over resource
attribute access exception raising.
:type raise_attr_exception: bool or tuple
:param cls engine: (optional). Engine that will be used to make
requests to Redmine.
- :param bool return_raw_response (optional). Whether engine should
return raw or json encoded responses.
"""
self.url = url.rstrip('/')
- self.ver = kwargs.get('version', None)
- self.date_format = kwargs.get('date_format', '%Y-%m-%d')
- self.datetime_format = kwargs.get('datetime_format',
'%Y-%m-%dT%H:%M:%SZ')
- self.raise_attr_exception = kwargs.get('raise_attr_exception', True)
+ self.ver = kwargs.pop('version', None)
+ self.date_format = kwargs.pop('date_format', '%Y-%m-%d')
+ self.datetime_format = kwargs.pop('datetime_format',
'%Y-%m-%dT%H:%M:%SZ')
+ self.raise_attr_exception = kwargs.pop('raise_attr_exception', True)
- engine = kwargs.get('engine', engines.DefaultEngine)
+ engine = kwargs.pop('engine', engines.DefaultEngine)
if not inspect.isclass(engine) or not issubclass(engine,
engines.BaseEngine):
raise exceptions.EngineClassError
@@ -87,18 +86,20 @@
finally:
self.engine = engine
- def upload(self, f):
+ def upload(self, f, filename=None):
"""
Uploads file from file path / file stream to Redmine and returns an
assigned token.
:param f: (required). File path / stream that will be uploaded.
:type f: string or file-like object
+ :param filename: (optional). Filename for the file that will be
uploaded.
"""
if self.ver is not None and LooseVersion(str(self.ver)) <
LooseVersion('1.4.0'):
raise exceptions.VersionMismatchError('File uploading')
url = '{0}/uploads.json'.format(self.url)
headers = {'Content-Type': 'application/octet-stream'}
+ params = {'filename': filename or ''}
# There're myriads of file-like object implementations here and there
and some of them don't have
# a "read" method, which is wrong, but that's what we have, on the
other hand it looks like all of
@@ -128,7 +129,7 @@
stream = open(f, 'rb')
close = True
- response = self.engine.request('post', url, data=stream,
headers=headers)
+ response = self.engine.request('post', url, params=params,
data=stream, headers=headers)
if close:
stream.close()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-redmine-2.2.1/redminelib/engines/base.py
new/python-redmine-2.3.0/redminelib/engines/base.py
--- old/python-redmine-2.2.1/redminelib/engines/base.py 2019-01-01
16:01:53.000000000 +0100
+++ new/python-redmine-2.3.0/redminelib/engines/base.py 2020-05-19
16:12:07.000000000 +0200
@@ -17,11 +17,18 @@
:param string password: (optional). Password used for authentication.
:param dict requests: (optional). Connection options.
:param string impersonate: (optional). Username to impersonate.
+ :param bool ignore_response (optional). If True no response processing
will be done at all.
+ :param bool return_response (optional). Whether to return response or
None.
:param bool return_raw_response (optional). Whether to return raw or
json encoded responses.
"""
+ self.ignore_response = options.pop('ignore_response', False)
+ self.return_response = options.pop('return_response', True)
self.return_raw_response = options.pop('return_raw_response', False)
self.requests = dict(dict(headers={}, params={}, data={}),
**options.get('requests', {}))
+ if self.ignore_response:
+ self.requests['stream'] = True
+
if options.get('impersonate') is not None:
self.requests['headers']['X-Redmine-Switch-User'] =
options['impersonate']
@@ -132,6 +139,9 @@
:param obj response: (required). Response object with response details.
"""
+ if self.ignore_response:
+ return None
+
if response.history:
r = response.history[0]
if r.is_redirect and r.request.url.startswith('http://') and
response.request.url.startswith('https://'):
@@ -140,7 +150,9 @@
status_code = response.status_code
if status_code in (200, 201, 204):
- if self.return_raw_response:
+ if not self.return_response:
+ return None
+ elif self.return_raw_response:
return response
elif not response.content.strip():
return True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-redmine-2.2.1/redminelib/managers/__init__.py
new/python-redmine-2.3.0/redminelib/managers/__init__.py
--- old/python-redmine-2.2.1/redminelib/managers/__init__.py 2018-05-02
13:33:02.000000000 +0200
+++ new/python-redmine-2.3.0/redminelib/managers/__init__.py 2020-05-20
15:05:32.000000000 +0200
@@ -3,4 +3,4 @@
"""
from .base import ResourceManager
-from .standard import WikiPageManager, FileManager
+from .standard import FileManager, WikiPageManager, UserManager, NewsManager
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-redmine-2.2.1/redminelib/managers/base.py
new/python-redmine-2.3.0/redminelib/managers/base.py
--- old/python-redmine-2.2.1/redminelib/managers/base.py 2019-01-04
10:08:54.000000000 +0100
+++ new/python-redmine-2.3.0/redminelib/managers/base.py 2020-05-21
12:35:24.000000000 +0200
@@ -2,7 +2,7 @@
Defines base Redmine resource manager class and it's infrastructure.
"""
-from .. import utilities, resultsets, exceptions
+from .. import resultsets, exceptions
class ResourceManager(object):
@@ -54,6 +54,22 @@
manager.params = params
return manager
+ def _construct_get_url(self, path):
+ """
+ Constructs URL for get method.
+
+ :param string path: absolute URL path.
+ """
+ return self.redmine.url + path
+
+ def _prepare_get_request(self, request):
+ """
+ Makes the necessary preparations for get request data.
+
+ :param dict request: Request data.
+ """
+ return self.resource_class.bulk_decode(request, self)
+
def get(self, resource_id, **params):
"""
Returns a Resource object from Redmine by resource id.
@@ -72,20 +88,31 @@
return resource
try:
- self.url = self.redmine.url +
self.resource_class.query_one.format(resource_id, **params)
- except KeyError as exception:
- raise exceptions.ValidationError('{0} argument is
required'.format(exception))
+ self.url =
self._construct_get_url(self.resource_class.query_one.format(resource_id,
**params))
+ except KeyError as e:
+ raise exceptions.ValidationError('{0} argument is
required'.format(e))
- self.params = self.resource_class.bulk_decode(params, self)
+ self.params = self._prepare_get_request(params)
self.container = self.resource_class.container_one
try:
- return self.to_resource(self.redmine.engine.request('get',
self.url, params=self.params)[self.container])
+ response = self.redmine.engine.request('get', self.url,
params=self.params)
except exceptions.ResourceNotFoundError as e:
if self.resource_class.requirements:
raise
exceptions.ResourceRequirementsError(self.resource_class.requirements)
raise e
+ return self._process_get_response(self.params, response)
+
+ def _process_get_response(self, request, response):
+ """
+ Processes get response and constructs resource object.
+
+ :param dict request: Original request data.
+ :param any response: Response received from Redmine for this request
data.
+ """
+ return self.to_resource(response[self.container])
+
def all(self, **params):
"""
Returns a ResourceSet object with all Resource objects.
@@ -149,17 +176,19 @@
if not fields:
raise exceptions.ResourceNoFieldsProvidedError
- formatter = utilities.MemorizeFormatter()
-
try:
- url =
self._construct_create_url(formatter.format(self.resource_class.query_create,
**fields))
+ url =
self._construct_create_url(self.resource_class.query_create.format(**fields))
except KeyError as e:
raise exceptions.ValidationError('{0} field is required'.format(e))
- self.params = formatter.used_kwargs
+ self.params = self.resource_class.query_create.formatter.used_kwargs
self.container = self.resource_class.container_create
- request = self._prepare_create_request(formatter.unused_kwargs)
+ request =
self._prepare_create_request(self.resource_class.query_create.formatter.unused_kwargs)
response =
self.redmine.engine.request(self.resource_class.http_method_create, url,
data=request)
+
+ if response is None:
+ return None
+
resource = self._process_create_response(request, response)
self.url = self.redmine.url +
self.resource_class.query_one.format(resource.internal_id, **fields)
return resource
@@ -203,22 +232,24 @@
if not fields:
raise exceptions.ResourceNoFieldsProvidedError
- formatter = utilities.MemorizeFormatter()
-
try:
- query_update = formatter.format(self.resource_class.query_update,
resource_id, **fields)
+ query_update =
self.resource_class.query_update.format(resource_id, **fields)
except KeyError as e:
param = e.args[0]
if param in self.params:
fields[param] = self.params[param]
- query_update =
formatter.format(self.resource_class.query_update, resource_id, **fields)
+ query_update =
self.resource_class.query_update.format(resource_id, **fields)
else:
raise exceptions.ValidationError('{0} argument is
required'.format(e))
url = self._construct_update_url(query_update)
- request = self._prepare_update_request(formatter.unused_kwargs)
+ request =
self._prepare_update_request(self.resource_class.query_update.formatter.unused_kwargs)
response =
self.redmine.engine.request(self.resource_class.http_method_update, url,
data=request)
+
+ if response is None:
+ return None
+
return self._process_update_response(request, response)
def _process_update_response(self, request, response):
@@ -264,6 +295,10 @@
request = self._prepare_delete_request(params)
response =
self.redmine.engine.request(self.resource_class.http_method_delete, url,
params=request)
+
+ if response is None:
+ return None
+
return self._process_delete_response(request, response)
def _process_delete_response(self, request, response):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-redmine-2.2.1/redminelib/managers/standard.py
new/python-redmine-2.3.0/redminelib/managers/standard.py
--- old/python-redmine-2.2.1/redminelib/managers/standard.py 2018-05-01
19:29:11.000000000 +0200
+++ new/python-redmine-2.3.0/redminelib/managers/standard.py 2020-05-21
12:35:18.000000000 +0200
@@ -6,6 +6,14 @@
from .. import exceptions
+class FileManager(ResourceManager):
+ def _process_create_response(self, request, response):
+ if response is True:
+ response = {self.container: {'id':
int(request[self.container]['token'].split('.')[0])}}
+
+ return super(FileManager, self)._process_create_response(request,
response)
+
+
class WikiPageManager(ResourceManager):
def _process_create_response(self, request, response):
if response is True:
@@ -14,9 +22,34 @@
return super(WikiPageManager, self)._process_create_response(request,
response)
-class FileManager(ResourceManager):
+class UserManager(ResourceManager):
+ @staticmethod
+ def _check_custom_url(path):
+ if path.endswith('/me.json'):
+ path = '/my/account.json'
+
+ return path
+
+ def _construct_get_url(self, path):
+ return super(UserManager,
self)._construct_get_url(self._check_custom_url(path))
+
+ def _prepare_create_request(self, request):
+ request = super(UserManager, self)._prepare_create_request(request)
+ request['send_information'] =
request[self.container].pop('send_information', False)
+ return request
+
+ def _construct_update_url(self, path):
+ return super(UserManager,
self)._construct_update_url(self._check_custom_url(path))
+
+ def _prepare_update_request(self, request):
+ request = super(UserManager, self)._prepare_update_request(request)
+ request['send_information'] =
request[self.resource_class.container_update].pop('send_information', False)
+ return request
+
+
+class NewsManager(ResourceManager):
def _process_create_response(self, request, response):
if response is True:
- response = {self.container: {'id':
int(request[self.container]['token'].split('.')[0])}}
+ response = {self.container:
self.redmine.news.filter(**self.params)[0].raw()}
- return super(FileManager, self)._process_create_response(request,
response)
+ return super(NewsManager, self)._process_create_response(request,
response)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-redmine-2.2.1/redminelib/resources/base.py
new/python-redmine-2.3.0/redminelib/resources/base.py
--- old/python-redmine-2.2.1/redminelib/resources/base.py 2019-01-04
10:57:02.000000000 +0100
+++ new/python-redmine-2.3.0/redminelib/resources/base.py 2020-05-21
13:27:59.000000000 +0200
@@ -19,6 +19,8 @@
which name starts with Base are considered base classes and not added to
the registry.
"""
def __new__(mcs, name, bases, attrs):
+ mcs.update_query_strings(attrs)
+
cls = super(Registrar, mcs).__new__(mcs, name, bases, attrs)
if name.startswith('Base'): # base classes shouldn't be added to the
registry
@@ -54,6 +56,17 @@
return registry[name].setdefault('class', cls)
@staticmethod
+ def update_query_strings(attrs):
+ """
+ Updates all `query_*` string attributes to use ResourceQueryFormatter
by default.
+ """
+ for k, v in attrs.items():
+ if k.startswith('query_') and v is not None:
+ attrs[k] = utilities.ResourceQueryStr(v)
+
+ return attrs
+
+ @staticmethod
def update_cls_attr(cls, name, value):
"""
Updates class attribute's value by first copying the current value and
then updating it with
@@ -97,6 +110,7 @@
query_update = None
query_delete = None
search_hints = None
+ extra_export_columns = []
http_method_create = 'post'
http_method_update = 'put'
http_method_delete = 'delete'
@@ -125,8 +139,8 @@
relations_includes = self._relations + self._includes
self.manager = manager
- self._create_readonly += relations_includes
- self._update_readonly += relations_includes
+ self._create_readonly = self._create_readonly[:] + relations_includes
+ self._update_readonly = self._update_readonly[:] + relations_includes
self._decoded_attrs = dict(dict.fromkeys(relations_includes),
**attributes)
self._encoded_attrs = {}
self._changes = {}
@@ -243,7 +257,8 @@
if attr == 'uploads':
for index, attachment in enumerate(value):
if 'token' not in attachment:
- value[index]['token'] =
manager.redmine.upload(attachment.pop('path', ''))['token']
+ value[index]['token'] = manager.redmine.upload(
+ attachment.pop('path', ''),
filename=attachment.get('filename'))['token']
return attr, value
elif attr == 'include' and isinstance(value, (list, tuple)):
@@ -370,7 +385,11 @@
self.post_update()
else:
self.pre_create()
- self._decoded_attrs = self.manager.create(**self._changes).raw()
+ resource = self.manager.create(**self._changes)
+
+ if resource is not None:
+ self._decoded_attrs = resource.raw()
+
self.post_create()
self._changes = {}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-redmine-2.2.1/redminelib/resources/standard.py
new/python-redmine-2.3.0/redminelib/resources/standard.py
--- old/python-redmine-2.2.1/redminelib/resources/standard.py 2019-02-22
13:00:08.000000000 +0100
+++ new/python-redmine-2.3.0/redminelib/resources/standard.py 2020-05-21
12:39:02.000000000 +0200
@@ -30,6 +30,7 @@
'news', 'issues', 'files']
_unconvertible = BaseResource._unconvertible + ['identifier', 'status']
_update_readonly = BaseResource._update_readonly + ['identifier']
+ _resource_map = {'default_version': 'Version'}
_resource_set_map = {
'custom_fields': 'CustomField',
'trackers': 'Tracker',
@@ -73,6 +74,7 @@
query_update = '/issues/{0}.json'
query_delete = '/issues/{0}.json'
search_hints = ['issue', 'issue closed']
+ extra_export_columns = ['description', 'last_notes']
_repr = [['id', 'subject'], ['title'], ['id']]
_includes = ['children', 'attachments', 'relations', 'changesets',
'journals', 'watchers']
@@ -201,12 +203,13 @@
redmine_version = '2.2'
container_filter = '{resource}'
query_filter = '/enumerations/{resource}.json'
+ query_url = '/enumerations/{0}/edit'
_resource_set_map = {'custom_fields': 'CustomField'}
@property
def url(self):
- return '{0}/enumerations/{1}/edit'.format(self.manager.redmine.url,
self.internal_id)
+ return self.manager.redmine.url +
self.query_url.format(self.internal_id)
class Attachment(BaseResource):
@@ -393,12 +396,14 @@
container_filter = 'users'
container_create = 'user'
container_update = 'user'
+ query_all_export = '/users.{format}'
query_all = '/users.json'
query_one = '/users/{0}.json'
query_filter = '/users.json'
query_create = '/users.json'
query_update = '/users/{0}.json'
query_delete = '/users/{0}.json'
+ manager_class = managers.UserManager
_repr = [['id', 'firstname', 'lastname'], ['id', 'name']]
_includes = ['memberships', 'groups']
@@ -485,24 +490,34 @@
class News(BaseResource):
redmine_version = '1.1'
container_all = 'news'
+ container_one = 'news'
container_filter = 'news'
+ container_create = 'news'
+ container_update = 'news'
query_all_export = '/news.{format}'
query_all = '/news.json'
+ query_one = '/news/{0}.json'
query_filter = '/news.json'
+ query_create = '/projects/{project_id}/news.json'
+ query_update = '/news/{0}.json'
+ query_delete = '/news/{0}.json'
+ query_url = '/news/{0}'
search_hints = ['news']
+ manager_class = managers.NewsManager
_repr = [['id', 'title']]
_resource_map = {'project': 'Project', 'author': 'User'}
@property
def url(self):
- return '{0}/news/{1}'.format(self.manager.redmine.url,
self.internal_id)
+ return self.manager.redmine.url +
self.query_url.format(self.internal_id)
class IssueStatus(BaseResource):
redmine_version = '1.3'
container_all = 'issue_statuses'
query_all = '/issue_statuses.json'
+ query_url = '/issue_statuses/{0}/edit'
_relations = ['issues']
_relations_name = 'status'
@@ -510,37 +525,40 @@
@property
def url(self):
- return '{0}/issue_statuses/{1}/edit'.format(self.manager.redmine.url,
self.internal_id)
+ return self.manager.redmine.url +
self.query_url.format(self.internal_id)
class Tracker(BaseResource):
redmine_version = '1.3'
container_all = 'trackers'
query_all = '/trackers.json'
+ query_url = '/trackers/{0}/edit'
_relations = ['issues']
_resource_set_map = {'issues': 'Issue'}
@property
def url(self):
- return '{0}/trackers/{1}/edit'.format(self.manager.redmine.url,
self.internal_id)
+ return self.manager.redmine.url +
self.query_url.format(self.internal_id)
class Query(BaseResource):
redmine_version = '1.3'
container_all = 'queries'
query_all = '/queries.json'
+ query_url = '/projects/{0}/issues?query_id={1}'
@property
def url(self):
- return '{0}/projects/{1}/issues?query_id={2}'.format(
- self.manager.redmine.url, self._decoded_attrs.get('project_id',
0), self.internal_id)
+ return self.manager.redmine.url + self.query_url.format(
+ self._decoded_attrs.get('project_id', 0), self.internal_id)
class CustomField(BaseResource):
redmine_version = '2.4'
container_all = 'custom_fields'
query_all = '/custom_fields.json'
+ query_url = '/custom_fields/{0}/edit'
_resource_set_map = {'trackers': 'Tracker', 'roles': 'Role'}
@@ -566,4 +584,4 @@
@property
def url(self):
- return '{0}/custom_fields/{1}/edit'.format(self.manager.redmine.url,
self.internal_id)
+ return self.manager.redmine.url +
self.query_url.format(self.internal_id)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-redmine-2.2.1/redminelib/resultsets.py
new/python-redmine-2.3.0/redminelib/resultsets.py
--- old/python-redmine-2.2.1/redminelib/resultsets.py 2019-01-04
10:42:57.000000000 +0100
+++ new/python-redmine-2.3.0/redminelib/resultsets.py 2020-05-06
19:11:49.000000000 +0200
@@ -6,7 +6,9 @@
import functools
import itertools
-from . import lookups, utilities, exceptions
+from distutils.version import LooseVersion
+
+from . import lookups, exceptions
class BaseResourceSet(object):
@@ -42,24 +44,49 @@
return self._total_count
- def export(self, fmt, savepath=None, filename=None):
+ def export(self, fmt, savepath=None, filename=None, columns=None,
encoding='UTF-8'):
"""
Exports all resources from resource set to requested format if
Resource supports that.
:param string fmt: (required). Format to use for export, e.g. atom,
csv, txt, pdf, html etc.
:param string savepath: (optional). Path where to save the file.
:param string filename: (optional). Name that will be used for the
file.
+ :param columns: (optional). Iterable of column names, "all_gui" for
GUI behaviour or "all" for all columns.
+ :param encoding: (optional). Encoding that will be used by Redmine for
the result file.
+ :type columns: iterable or string
"""
if self.manager.resource_class.query_all_export is None:
raise exceptions.ExportNotSupported
- formatter = utilities.MemorizeFormatter()
+ url = self.manager.redmine.url +
self.manager.resource_class.query_all_export.format(
+ format=fmt, **self.manager.params)
+
+ params =
dict(self.manager.resource_class.query_all_export.formatter.unused_kwargs,
encoding=encoding)
- url = self.manager.redmine.url + formatter.format(
- self.manager.resource_class.query_all_export, format=fmt,
**self.manager.params)
+ if columns is not None:
+ if columns == 'all':
+ columns = ['all', 'all_inline'] +
self.manager.resource_class.extra_export_columns
+
+ if self.manager.redmine.ver is not None and
LooseVersion(str(self.manager.redmine.ver)) < '3.4.0':
+
params.update(dict.fromkeys(self.manager.resource_class.extra_export_columns,
1), columns='all')
+ elif 'all_gui' in columns:
+ if columns == 'all_gui':
+ columns = ['all', 'all_inline']
+
+ if self.manager.redmine.ver is not None and
LooseVersion(str(self.manager.redmine.ver)) < '3.4.0':
+ params['columns'] = 'all'
+ else:
+ if self.manager.redmine.ver is not None and
LooseVersion(str(self.manager.redmine.ver)) < '3.4.0':
+ params.update(dict.fromkeys(columns, 1), columns='all')
+
+ columns = list(columns) + ['all', 'all_inline']
+
+ # Redmine >= 3.4.0 happily accepts c[] array with column names
+ # for older versions the above hack with params is being used
+ params['c[]'] = columns
try:
- return self.manager.redmine.download(url, savepath, filename,
params=formatter.unused_kwargs)
+ return self.manager.redmine.download(url, savepath, filename,
params=params)
except exceptions.UnknownError as e:
if e.status_code == 406:
raise exceptions.ExportFormatNotSupportedError
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-redmine-2.2.1/redminelib/utilities.py
new/python-redmine-2.3.0/redminelib/utilities.py
--- old/python-redmine-2.2.1/redminelib/utilities.py 2019-01-01
19:47:22.000000000 +0100
+++ new/python-redmine-2.3.0/redminelib/utilities.py 2019-08-18
15:53:53.000000000 +0200
@@ -7,6 +7,11 @@
import string
import functools
+try:
+ from urllib.parse import quote
+except ImportError:
+ from urllib import quote
+
def fix_unicode(cls):
"""
@@ -65,9 +70,9 @@
return result
-class MemorizeFormatter(string.Formatter):
+class ResourceQueryFormatter(string.Formatter):
"""
- Memorizes all arguments, used during string formatting.
+ Quotes query and memorizes all arguments, used during string formatting.
"""
def __init__(self):
self.used_kwargs = {}
@@ -79,3 +84,16 @@
self.used_kwargs[item] = kwargs.pop(item)
self.unused_kwargs = kwargs
+
+ def format_field(self, value, format_spec):
+ return quote(super(ResourceQueryFormatter, self).format_field(value,
format_spec).encode('utf-8'))
+
+
+class ResourceQueryStr(str):
+ """
+ Extends default string with additional formatting capabilities.
+ """
+ formatter = ResourceQueryFormatter()
+
+ def format(self, *args, **kwargs):
+ return self.formatter.format(self, *args, **kwargs)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-redmine-2.2.1/redminelib/version.py
new/python-redmine-2.3.0/redminelib/version.py
--- old/python-redmine-2.2.1/redminelib/version.py 2019-02-28
12:40:05.000000000 +0100
+++ new/python-redmine-2.3.0/redminelib/version.py 2020-05-21
13:45:19.000000000 +0200
@@ -1 +1 @@
-__version__ = '2.2.1'
+__version__ = '2.3.0'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-redmine-2.2.1/setup.py
new/python-redmine-2.3.0/setup.py
--- old/python-redmine-2.2.1/setup.py 2019-01-12 12:44:05.000000000 +0100
+++ new/python-redmine-2.3.0/setup.py 2020-05-07 12:35:55.000000000 +0200
@@ -22,7 +22,7 @@
tests_require = ['nose', 'coverage']
-if sys.version_info[:2] < (3, 3):
+if sys.version_info[:2] == (2, 7):
tests_require.append('mock')
exec(open('redminelib/version.py').read())
@@ -41,8 +41,8 @@
description='Library for communicating with a Redmine project management
application',
long_description=open('README.rst').read() + '\n\n' +
open('CHANGELOG.rst').read(),
keywords='redmine redmineup redminecrm redminelib easyredmine',
- python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
- install_requires=['requests>=2.20.0'],
+ python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*',
+ install_requires=['requests>=2.23.0'],
tests_require=tests_require,
cmdclass={'test': NoseTests},
zip_safe=False,
@@ -57,10 +57,10 @@
'Environment :: Web Environment',
'Operating System :: OS Independent',
'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
+ 'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy'
],
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-redmine-2.2.1/tests/responses/standard.py
new/python-redmine-2.3.0/tests/responses/standard.py
--- old/python-redmine-2.2.1/tests/responses/standard.py 2018-05-01
16:26:13.000000000 +0200
+++ new/python-redmine-2.3.0/tests/responses/standard.py 2020-05-08
15:41:47.000000000 +0200
@@ -24,6 +24,7 @@
},
'wiki_page': {
'get': {'wiki_page': {'title': 'Foo', 'version': 1}},
+ 'get_special': {'wiki_page': {'title': 'Foo%Bar', 'version': 1}},
'filter': {'wiki_pages': [{'title': 'Foo', 'version': 1}, {'title':
'Bar', 'version': 2}]},
},
'project_membership': {
@@ -56,8 +57,9 @@
'all': {'roles': [{'name': 'Foo', 'id': 1}, {'name': 'Bar', 'id': 2}]},
},
'news': {
- 'all': {'news': [{'title': 'Foo', 'id': 1}, {'title': 'Bar', 'id':
2}]},
- 'filter': {'news': [{'title': 'Foo', 'id': 1}, {'title': 'Bar', 'id':
2}]},
+ 'get': {'news': {'title': 'Foo', 'id': 1}},
+ 'all': {'news': [{'title': 'Foo', 'id': 2}, {'title': 'Bar', 'id':
1}]},
+ 'filter': {'news': [{'title': 'Foo', 'id': 2}, {'title': 'Bar', 'id':
1}]},
},
'issue_status': {
'all': {'issue_statuses': [{'name': 'Foo', 'id': 1}, {'name': 'Bar',
'id': 2}]},
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-redmine-2.2.1/tests/test_engines.py
new/python-redmine-2.3.0/tests/test_engines.py
--- old/python-redmine-2.2.1/tests/test_engines.py 2017-04-10
20:53:44.000000000 +0200
+++ new/python-redmine-2.3.0/tests/test_engines.py 2020-05-19
16:12:07.000000000 +0200
@@ -29,6 +29,17 @@
self.response.content = ''
self.assertEqual(self.redmine.engine.request('put', self.url), True)
+ def test_returns_none_with_ignore_response_true(self):
+ with self.redmine.session(ignore_response=True):
+ self.assertEqual(self.redmine.engine.ignore_response, True)
+ self.assertEqual(self.redmine.engine.requests['stream'], True)
+ self.assertEqual(self.redmine.engine.request('post', self.url),
None)
+
+ def test_returns_none_with_return_response_false(self):
+ with self.redmine.session(return_response=False):
+ self.assertEqual(self.redmine.engine.return_response, False)
+ self.assertEqual(self.redmine.engine.request('post', self.url),
None)
+
def test_session_not_implemented_exception(self):
self.assertRaises(NotImplementedError, lambda: engines.BaseEngine())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-redmine-2.2.1/tests/test_managers.py
new/python-redmine-2.3.0/tests/test_managers.py
--- old/python-redmine-2.2.1/tests/test_managers.py 2019-01-04
11:18:43.000000000 +0100
+++ new/python-redmine-2.3.0/tests/test_managers.py 2020-05-20
16:20:12.000000000 +0200
@@ -12,7 +12,7 @@
class ResourceManagerTestCase(BaseRedmineTestCase):
def test_has_custom_repr(self):
- self.assertEqual(repr(self.redmine.user),
'<redminelib.managers.ResourceManager object for User resource>')
+ self.assertEqual(repr(self.redmine.issue),
'<redminelib.managers.ResourceManager object for Issue resource>')
def test_supports_additional_resources(self):
self.assertIsInstance(self.redmine.foo_resource,
managers.ResourceManager)
@@ -136,6 +136,10 @@
self.assertEqual(project._decoded_attrs, defaults)
self.assertEqual(repr(project), '<redminelib.resources.Project #0 "">')
+ def test_create_resource_returns_none(self):
+ with self.redmine.session(return_response=False):
+ self.assertEqual(self.redmine.user.create(firstname='John',
lastname='Smith'), None)
+
def test_update_resource(self):
self.response.content = ''
manager = self.redmine.wiki_page
@@ -165,6 +169,10 @@
self.assertEquals(len(w), 1)
self.assertIs(w[0].category, exceptions.PerformanceWarning)
+ def test_update_resource_returns_none(self):
+ with self.redmine.session(return_response=False):
+ self.assertEqual(self.redmine.issue.update(1, subject='Bar'), None)
+
def test_delete_resource(self):
self.response.content = ''
self.assertEqual(self.redmine.wiki_page.delete(b'\xcf\x86oo'.decode('utf-8'),
project_id=1), True)
@@ -174,6 +182,10 @@
self.response.content = ''
self.assertEqual(self.redmine.wiki_page.delete(b'\xcf\x86oo'.decode('utf-8'),
project_id=1), True)
+ def test_delete_resource_returns_none(self):
+ with self.redmine.session(return_response=False):
+ self.assertEqual(self.redmine.user.delete(1), None)
+
def test_resource_get_method_unsupported_exception(self):
self.assertRaises(exceptions.ResourceBadMethodError, lambda:
self.redmine.issue_journal.get(1))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-redmine-2.2.1/tests/test_redmine.py
new/python-redmine-2.3.0/tests/test_redmine.py
--- old/python-redmine-2.2.1/tests/test_redmine.py 2019-01-04
11:18:43.000000000 +0100
+++ new/python-redmine-2.3.0/tests/test_redmine.py 2020-05-21
13:33:28.000000000 +0200
@@ -62,7 +62,7 @@
def test_successful_file_upload(self):
self.response.status_code = 201
self.response.json.return_value = {'upload': {'id': 1, 'token':
'123456'}}
- self.assertEqual(self.redmine.upload('foo')['token'], '123456')
+ self.assertEqual(self.redmine.upload('foo',
filename='foo.jpg')['token'], '123456')
def test_successful_filestream_upload(self):
from io import StringIO
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python-redmine-2.2.1/tests/test_resources_standard.py
new/python-redmine-2.3.0/tests/test_resources_standard.py
--- old/python-redmine-2.2.1/tests/test_resources_standard.py 2018-05-01
19:29:11.000000000 +0200
+++ new/python-redmine-2.3.0/tests/test_resources_standard.py 2020-05-20
19:33:58.000000000 +0200
@@ -702,6 +702,17 @@
wiki_page = self.redmine.wiki_page.get('Foo', project_id=1)
self.assertEqual(wiki_page.title, 'Foo')
+ def test_wiki_page_get_special(self):
+ """Test getting a wiki page with special char in title."""
+ self.response.json.return_value = responses['wiki_page']['get_special']
+ wiki_page = self.redmine.wiki_page.get('Foo%Bar', project_id=1)
+ self.assertEqual(
+ self.patch_requests.call_args[0][1],
+ '{0}/projects/1/wiki/Foo%25Bar.json'.format(self.url)
+ )
+ self.assertEqual(wiki_page.title, 'Foo%Bar')
+ self.assertEqual(wiki_page.url,
'http://foo.bar/projects/1/wiki/Foo%25Bar')
+
def test_wiki_page_filter(self):
self.response.json.return_value = responses['wiki_page']['filter']
wiki_pages = self.redmine.wiki_page.filter(project_id=1)
@@ -714,6 +725,17 @@
wiki_page = self.redmine.wiki_page.create(project_id='foo',
title='Foo')
self.assertEqual(wiki_page.title, 'Foo')
+ def test_wiki_page_create_special(self):
+ """Test creating a wiki page with special char in title."""
+ self.response.status_code = 201
+ self.response.json.return_value = responses['wiki_page']['get_special']
+ wiki_page = self.redmine.wiki_page.create(project_id='foo',
title='Foo%Bar')
+ self.assertEqual(
+ self.patch_requests.call_args[0][1],
+ '{0}/projects/foo/wiki/Foo%25Bar.json'.format(self.url)
+ )
+ self.assertEqual(wiki_page.title, 'Foo%Bar')
+
def test_wiki_page_delete(self):
self.response.json.return_value = responses['wiki_page']['get']
wiki_page = self.redmine.wiki_page.get('Foo', project_id=1)
@@ -996,6 +1018,12 @@
self.assertEqual(user.id, 1)
self.assertEqual(user.firstname, 'John')
+ def test_user_get_account(self):
+ self.response.json.return_value = responses['user']['get']
+ user = self.redmine.user.get('me')
+ self.assertEqual(user.firstname, 'John')
+
self.assertTrue(self.patch_requests.call_args[0][1].endswith('/my/account.json'))
+
def test_user_all(self):
self.response.json.return_value = responses['user']['all']
users = self.redmine.user.all()
@@ -1019,6 +1047,13 @@
self.assertEqual(user.firstname, 'John')
self.assertEqual(user.lastname, 'Smith')
+ def test_user_create_with_send_information(self):
+ import json
+ self.response.status_code = 201
+ self.response.json.return_value = responses['user']['get']
+ self.redmine.user.create(firstname='John', lastname='Smith',
send_information=True)
+
self.assertEqual(json.loads(self.patch_requests.call_args[1]['data'])['send_information'],
True)
+
def test_user_delete(self):
self.response.json.return_value = responses['user']['get']
user = self.redmine.user.get(1)
@@ -1033,6 +1068,16 @@
user.firstname = 'Bar'
self.assertIsInstance(user.save(), resources.User)
+ def test_user_update_account(self):
+ self.redmine.user.update('me', lastname='Foo', firstname='Bar')
+
self.assertTrue(self.patch_requests.call_args[0][1].endswith('/my/account.json'))
+
+ def test_user_update_with_send_information(self):
+ import json
+ self.response.json.return_value = responses['user']['get']
+ self.redmine.user.update(1, firstname='John', lastname='Smith',
send_information=True)
+
self.assertEqual(json.loads(self.patch_requests.call_args[1]['data'])['send_information'],
True)
+
def test_user_custom_str(self):
self.response.json.return_value = responses['user']['get']
self.assertEqual(str(self.redmine.user.get(1)), 'John Smith')
@@ -1167,7 +1212,7 @@
self.assertEqual(self.redmine.news.resource_class.redmine_version,
'1.1')
def test_news_get(self):
- self.response.json.return_value = responses['news']['all']
+ self.response.json.return_value = responses['news']['get']
news = self.redmine.news.get(1)
self.assertEqual(news.id, 1)
self.assertEqual(news.title, 'Foo')
@@ -1175,22 +1220,49 @@
def test_news_all(self):
self.response.json.return_value = responses['news']['all']
news = self.redmine.news.all()
- self.assertEqual(news[0].id, 1)
+ self.assertEqual(news[0].id, 2)
self.assertEqual(news[0].title, 'Foo')
- self.assertEqual(news[1].id, 2)
+ self.assertEqual(news[1].id, 1)
self.assertEqual(news[1].title, 'Bar')
def test_news_filter(self):
self.response.json.return_value = responses['news']['filter']
news = self.redmine.news.filter(project_id=1)
- self.assertEqual(news[0].id, 1)
+ self.assertEqual(news[0].id, 2)
self.assertEqual(news[0].title, 'Foo')
- self.assertEqual(news[1].id, 2)
+ self.assertEqual(news[1].id, 1)
self.assertEqual(news[1].title, 'Bar')
+ def test_news_create(self):
+ self.response.status_code = 201
+ self.response.json.return_value = responses['news']['get']
+ news = self.redmine.news.create(project_id=1, title='Foo')
+ self.assertEqual(news.title, 'Foo')
+
+ def test_news_create_empty_response(self):
+ self.set_patch_side_effect([
+ mock.Mock(status_code=204, history=[], content=''),
+ mock.Mock(status_code=201, history=[], **{'json.return_value':
responses['news']['filter']})
+ ])
+ news = self.redmine.news.create(project_id=1, title='Foo')
+ self.assertEqual(news.title, 'Foo')
+
+ def test_news_delete(self):
+ self.response.json.return_value = responses['news']['get']
+ news = self.redmine.news.get(1)
+ self.response.content = ''
+ self.assertEqual(news.delete(), True)
+ self.assertEqual(self.redmine.news.delete(1), True)
+
+ def test_news_update(self):
+ self.response.json.return_value = responses['news']['get']
+ news = self.redmine.news.get(1)
+ news.title = 'Bar'
+ self.assertIsInstance(news.save(), resources.News)
+
def test_news_url(self):
self.response.json.return_value = responses['news']['filter']
- self.assertEqual(self.redmine.news.filter(project_id=1)[0].url,
'{0}/news/1'.format(self.url))
+ self.assertEqual(self.redmine.news.filter(project_id=1)[0].url,
'{0}/news/2'.format(self.url))
@mock.patch('redminelib.open', mock.mock_open(), create=True)
def test_news_export(self):
@@ -1204,7 +1276,7 @@
def test_news_repr(self):
self.response.json.return_value = responses['news']['filter']
- self.assertEqual(repr(self.redmine.news.filter(project_id=1)[0]),
'<redminelib.resources.News #1 "Foo">')
+ self.assertEqual(repr(self.redmine.news.filter(project_id=1)[0]),
'<redminelib.resources.News #2 "Foo">')
def test_issue_status_version(self):
self.assertEqual(self.redmine.issue_status.resource_class.redmine_version,
'1.3')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-redmine-2.2.1/tests/test_resultsets.py
new/python-redmine-2.3.0/tests/test_resultsets.py
--- old/python-redmine-2.2.1/tests/test_resultsets.py 2018-04-29
19:17:19.000000000 +0200
+++ new/python-redmine-2.3.0/tests/test_resultsets.py 2020-05-06
19:25:41.000000000 +0200
@@ -178,6 +178,32 @@
self.response.iter_content = lambda chunk_size: (str(num) for num in
range(0, 5))
self.assertEqual(self.redmine.issue.all().export('txt', '/foo/bar'),
'/foo/bar/issues.txt')
+ @mock.patch('redminelib.open', mock.mock_open(), create=True)
+ def test_export_with_all_columns(self):
+ self.response.iter_content = lambda chunk_size: (str(num) for num in
range(0, 5))
+ self.assertEqual(self.redmine.issue.all().export('txt', '/foo/bar',
columns='all'), '/foo/bar/issues.txt')
+ self.redmine.ver = '3.3.0'
+ self.assertEqual(self.redmine.issue.all().export('txt', '/foo/bar',
columns='all'), '/foo/bar/issues.txt')
+
+ @mock.patch('redminelib.open', mock.mock_open(), create=True)
+ def test_export_with_all_gui_columns(self):
+ self.response.iter_content = lambda chunk_size: (str(num) for num in
range(0, 5))
+ self.assertEqual(self.redmine.issue.all().export('txt', '/foo/bar',
columns='all_gui'), '/foo/bar/issues.txt')
+ self.redmine.ver = '3.3.0'
+ self.assertEqual(self.redmine.issue.all().export('txt', '/foo/bar',
columns='all_gui'), '/foo/bar/issues.txt')
+
+ @mock.patch('redminelib.open', mock.mock_open(), create=True)
+ def test_export_with_all_gui_extra_columns(self):
+ self.response.iter_content = lambda chunk_size: (str(num) for num in
range(0, 5))
+ self.assertEqual(self.redmine.issue.all().export('txt', '/foo/bar',
columns=['all_gui']), '/foo/bar/issues.txt')
+ self.redmine.ver = '3.3.0'
+ self.assertEqual(self.redmine.issue.all().export('txt', '/foo/bar',
columns=['all_gui']), '/foo/bar/issues.txt')
+
+ @mock.patch('redminelib.open', mock.mock_open(), create=True)
+ def test_export_with_custom_columns(self):
+ self.response.iter_content = lambda chunk_size: (str(num) for num in
range(0, 5))
+ self.assertEqual(self.redmine.issue.all().export('txt', '/foo/bar',
columns=['status']), '/foo/bar/issues.txt')
+
def test_export_not_supported_exception(self):
self.assertRaises(exceptions.ExportNotSupported, lambda:
self.redmine.custom_field.all().export('pdf'))