Skip to content

Commit

Permalink
Merge pull request #134 from arXiv/develop
Browse files Browse the repository at this point in the history
Pre-release merge for v0.15.8
  • Loading branch information
mhl10 authored Jun 7, 2019
2 parents cc9eb8f + c80a003 commit 6149aa1
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 35 deletions.
4 changes: 2 additions & 2 deletions arxiv/base/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@

ARXIV_BUSINESS_TZ = os.environ.get("ARXIV_BUSINESS_TZ", "US/Eastern")

BASE_VERSION = "0.15.7"
BASE_VERSION = "0.15.8"
"""The version of the arxiv-base package."""

APP_VERSION = "0.15.7"
APP_VERSION = "0.15.8"
"""The version of the base test app."""

"""
Expand Down
25 changes: 24 additions & 1 deletion arxiv/base/middleware/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,23 @@ def my_route():
"""

from typing import Type, Callable, List, Union
import warnings
from flask import Flask

from .base import BaseMiddleware, IWSGIMiddlewareFactory, IWSGIApp
from .. import logging

logger = logging.getLogger(__name__)


def wrap(app: Flask, middlewares: List[IWSGIMiddlewareFactory]) -> Callable:
"""
Wrap a :class:`.Flask` app in WSGI middlewares.
Adds/updates ``app.middlewares: Dict[str, IWSGIApp]`` so that middleware
instances can be accessed later on. Keys are the ``__name__``s of the
middleware class/factory.
Parameters
----------
app : :class:`.Flask`
Expand All @@ -98,13 +106,28 @@ def wrap(app: Flask, middlewares: List[IWSGIMiddlewareFactory]) -> Callable:
"""
if not hasattr(app, 'wsgi_app'):
raise TypeError('Not a valid Flask app or middleware')

if not hasattr(app, 'middlewares'):
app.middlewares = {}

# Apply the last middleware first, so that the first middleware is called
# first upon the request.
wrapped_app: IWSGIApp = app.wsgi_app
for middleware in middlewares[::-1]:
try:
wrapped_app = middleware(wrapped_app, config=app.config)
except TypeError: # Maintain backward compatibility.
except TypeError as e:
# Maintain backward compatibility with middlewares that don't
# accept kwargs.
logger.debug('Encountered TypeError while initializing'
' midleware: %s', e)
warnings.warn('Middlewares that do not accept kwargs are'
' deprecated. You should update your middleware'
' to accept arbitrary kwargs', DeprecationWarning)
wrapped_app = middleware(wrapped_app)

key = getattr(middleware, '__name__', str(middleware))
app.middlewares[key] = wrapped_app

app.wsgi_app = wrapped_app # type: ignore
return app
2 changes: 1 addition & 1 deletion arxiv/base/middleware/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def wsgi_app(self) -> IWSGIApp:


class IWSGIMiddlewareFactory(Protocol):
"""Defines a minimal WSGI middlware factory."""
"""Defines a minimal WSGI middleware factory."""

def __call__(self, app: IWSGIApp, config: Mapping = {}) -> IWSGIMiddleware:
"""Generate a :class:`.WSGIMiddleware`."""
Expand Down
28 changes: 20 additions & 8 deletions arxiv/integration/api/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
handling that is invariant (or nearly so) among service integration modules
that target HTTP APIs.
.. todo::
Make retry parameters easier to configure.
Specific goals
--------------
Expand All @@ -15,9 +19,6 @@
- Handle binding a service instance (with its persistent session) to the
Flask application context.
.. todo::
Make retry parameters easier to configure.
"""

from typing import Optional, Tuple, MutableMapping, List, Any
Expand Down Expand Up @@ -88,9 +89,16 @@ def get_something(self, anything: str, token: str) -> dict:
@app.route('/foo')
def foo():
MyCoolIntegration.get_something('foo', request.environ['token'])
mci = MyCoolIntegration.current_session()
mci.get_something('foo', request.environ['token'])
...
Any additional parameters in your app config that start with the
``service_name`` in upper case will be passed in as kwargs to the
constructor. By default, these are stored on ``._extra`` for internal
use.
"""

class Meta:
Expand All @@ -103,7 +111,7 @@ class Meta:
service_name = "base"

def __init__(self, endpoint: str, verify: bool = True,
headers: dict = {}) -> None:
headers: dict = {}, **extra: Any) -> None:
"""
Initialize an HTTP session.
Expand All @@ -115,8 +123,10 @@ def __init__(self, endpoint: str, verify: bool = True,
Whether or not SSL certificate verification should enforced.
headers : dict
Headers to be included on all requests.
extra : kwargs
"""
self._extra = extra
self._session = requests.Session()
self._verify = verify
self._retry = Retry(
Expand Down Expand Up @@ -236,12 +246,14 @@ def get_session(cls, app: Optional[Flask] = None) -> 'HTTPIntegration':
app = current_app
name = cls.Meta.service_name.upper()
try:
endpoint = app.config[f'{name}_ENDPOINT']
verify = app.config[f'{name}_VERIFY']
params = app.config.get_namespace(f'{name}_') # type: ignore
endpoint = params.pop('endpoint')
verify = params.pop('verify')
except KeyError as e:
raise RuntimeError('Must call init_app() on app before use') from e

logger.debug('Create %s session at endpoint %s', name, endpoint)
return cls(endpoint, verify=verify)
return cls(endpoint, verify=verify, **params)

@classmethod
def current_session(cls) -> 'HTTPIntegration':
Expand Down
66 changes: 44 additions & 22 deletions arxiv/taxonomy/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,8 @@
'cs.NA': {
'name': 'Numerical Analysis',
'in_archive': 'cs',
'description': 'Roughly includes material in ACM Subject Class G.1.',
'description': 'cs.NA is an alias for math.NA. Roughly includes '
'material in ACM Subject Class G.1.',
'is_active': True
},
'cs.NE': {
Expand Down Expand Up @@ -903,18 +904,19 @@
'cs.SY': {
'name': 'Systems and Control',
'in_archive': 'cs',
'description': 'This section includes theoretical and experimental '
'research covering all facets of automatic control '
'systems, having as focal point analysis and design '
'methods using tools of modeling, simulation and '
'optimization. Specific areas of research include '
'nonlinear, distributed, adaptive, stochastic and '
'robust control, hybrid and discrete event systems. '
'Application areas include automotive, aerospace, '
'process control, network control, biological systems, '
'multiagent and cooperative control, sensor networks, '
'control of cyberphysical and energy-related systems, '
'control of computing systems.',
'description': 'cs.SY is an alias for eess.SY. This section includes '
'theoretical and experimental research covering all '
'facets of automatic control systems, having as focal '
'point analysis and design methods using tools of '
'modeling, simulation and optimization. Specific areas '
'of research include nonlinear, distributed, adaptive, '
'stochastic and robust control, hybrid and discrete '
'event systems. Application areas include automotive, '
'aerospace, process control, network control, '
'biological systems, multiagent and cooperative '
'control, sensor networks, control of cyberphysical '
'and energy-related systems, control of computing '
'systems.',
'is_active': True
},
'dg-ga': {
Expand Down Expand Up @@ -1031,6 +1033,23 @@
'of Things.',
'is_active': True
},
'eess.SY': {
'name': 'Systems and Control',
'in_archive': 'eess',
'description': 'This section includes theoretical and experimental '
'research covering all facets of automatic control '
'systems, having as focal point analysis and design '
'methods using tools of modeling, simulation and '
'optimization. Specific areas of research include '
'nonlinear, distributed, adaptive, stochastic and '
'robust control, hybrid and discrete event systems. '
'Application areas include automotive, aerospace, '
'process control, network control, biological systems, '
'multiagent and cooperative control, sensor networks, '
'control of cyberphysical and energy-related systems, '
'control of computing systems.',
'is_active': True
},
'funct-an': {
'name': 'Functional Analysis',
'in_archive': 'funct-an',
Expand Down Expand Up @@ -1226,9 +1245,10 @@
'math.MP': {
'name': 'Mathematical Physics',
'in_archive': 'math',
'description': 'Mathematical methods in quantum field theory, quantum '
'mechanics, statistical mechanics, condensed matter, '
'nuclear and atomic physics',
'description': 'math.MP is an alias for math-ph. Mathematical methods '
'in quantum field theory, quantum mechanics, '
'statistical mechanics, condensed matter, nuclear and '
'atomic physics.',
'is_active': True
},
'math.NA': {
Expand Down Expand Up @@ -1311,9 +1331,8 @@
'math.ST': {
'name': 'Statistics Theory',
'in_archive': 'math',
'description': 'math.ST is an alias for stat.TH. Applied, '
'computational and theoretical statistics: e.g. '
'statistical inference, regression, time series, '
'description': 'Applied, computational and theoretical statistics: '
'e.g. statistical inference, regression, time series, '
'multivariate analysis, data analysis, Markov chain '
'Monte Carlo, design of experiments, case studies',
'is_active': True
Expand Down Expand Up @@ -1704,8 +1723,9 @@
'stat.TH': {
'name': 'Statistics Theory',
'in_archive': 'stat',
'description': 'Asymptotics, Bayesian Inference, Decision Theory, '
'Estimation, Foundations, Inference, Testing',
'description': 'stat.TH is an alias for math.ST. Asymptotics, '
'Bayesian Inference, Decision Theory, Estimation, '
'Foundations, Inference, Testing.',
'is_active': True
},
'supr-con': {
Expand Down Expand Up @@ -1763,7 +1783,9 @@
'math.MP': 'math-ph',
'stat.TH': 'math.ST',
'math.IT': 'cs.IT',
'q-fin.EC': 'econ.GN'
'q-fin.EC': 'econ.GN',
'cs.SY': 'eess.SY',
'cs.NA': 'math.NA'
}
"""
Equivalences: category alias: canonical category
Expand Down
5 changes: 5 additions & 0 deletions arxiv/taxonomy/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ def test_aliases(self):
self.assertIn(key, CATEGORIES)
self.assertIn(value, CATEGORIES)

self.assertIn(f'{key} is an alias for {value}.',
CATEGORIES[key]['description'],
'Category description for an alias should indicate '
'that it is an alias for some other category')

category = Category(key)
self.assertIsInstance(category.unalias(), Category)
self.assertNotEqual(category.unalias(), category)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setup(
name='arxiv-base',
version='0.15.7',
version='0.15.8',
packages=[f'arxiv.{package}' for package
in find_packages('arxiv', exclude=['*test*'])],
zip_safe=False,
Expand Down

0 comments on commit 6149aa1

Please sign in to comment.