Skip to content

Commit ac7919e

Browse files
authored
test/support a wider range of pythons and djangoes (#82)
* add github actions workflow * test more pythons and djangoes * drop python2 support * [wip] django 3+ * [wip] elasticsearch-dsl-py 6.3+ * fix lint * add testbox to docker-compose.yml * more pythons and more djangoes * update versions * test a metric with custom analyzer * better testbox * update versions * quotes * use appconfig in test settings * update version * goodbye travis
1 parent 8a02ffc commit ac7919e

File tree

16 files changed

+199
-152
lines changed

16 files changed

+199
-152
lines changed

.github/workflows/test_djelme.yml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: test_djelme
2+
3+
on:
4+
push:
5+
pull_request:
6+
workflow_dispatch:
7+
8+
permissions:
9+
checks: write
10+
11+
jobs:
12+
lint:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v3
16+
- uses: actions/setup-python@v4
17+
id: setup-py
18+
with:
19+
python-version: '3.7'
20+
- run: pip install -U tox
21+
- name: alias pythonX.Y for tox
22+
run: alias python${{ steps.setup-py.outputs.python-version }}=${{ steps.setup-py.outputs.python-path }}
23+
- run: TOXENV=lint tox
24+
25+
26+
test:
27+
strategy:
28+
matrix:
29+
python: ['3.6', '3.7', '3.8', '3.9', '3.10']
30+
django: ['1.11', '2.0', '2.1', '2.2', '3.0', '3.1', '3.2', '4.0', '4.1']
31+
# TODO: elasticsearch: ['6', '7', '8', '9']
32+
exclude:
33+
- {python: '3.6', django: '4.0'}
34+
- {python: '3.6', django: '4.1'}
35+
- {python: '3.7', django: '4.0'}
36+
- {python: '3.7', django: '4.1'}
37+
- {python: '3.10', django: '1.11'}
38+
- {python: '3.10', django: '2.0'}
39+
runs-on: ubuntu-latest
40+
services:
41+
elasticsearch:
42+
image: elasticsearch:6.8.23
43+
ports:
44+
- 9201:9200
45+
steps:
46+
- uses: actions/checkout@v3
47+
- uses: actions/setup-python@v4
48+
id: setup-py
49+
with:
50+
python-version: ${{ matrix.python }}
51+
- run: pip install -U tox
52+
- run: alias python${{ steps.setup-py.outputs.python-version }}=${{ steps.setup-py.outputs.python-path }}
53+
- run: TOXENV=`echo 'py${{ matrix.python }}-django${{matrix.django}}' | sed 's/\.//g'` tox
54+

.pre-commit-config.yaml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
repos:
2-
- repo: https://github.com/ambv/black
3-
rev: 18.9b0
2+
- repo: https://github.com/psf/black
3+
rev: 22.8.0
44
hooks:
55
- id: black
6-
language_version: python3.6
7-
- repo: https://github.com/pre-commit/pre-commit-hooks
8-
rev: v2.1.0
6+
language_version: python3.7
7+
- repo: https://github.com/PyCQA/flake8
8+
rev: 5.0.4
99
hooks:
1010
- id: flake8
11-
additional_dependencies: ["flake8-bugbear==18.8.0"]
11+
additional_dependencies: ["flake8-bugbear==22.8.23"]
1212
- repo: https://github.com/asottile/blacken-docs
13-
rev: v0.3.0
13+
rev: v1.12.1
1414
hooks:
1515
- id: blacken-docs
16-
additional_dependencies: [black==18.9b0]
16+
additional_dependencies: [black==22.8.0]

.travis.yml

Lines changed: 0 additions & 73 deletions
This file was deleted.

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# django-elasticsearch-metrics
22

33
[![pypi](https://badge.fury.io/py/django-elasticsearch-metrics.svg)](https://badge.fury.io/py/django-elasticsearch-metrics)
4-
[![Build Status](https://travis-ci.org/CenterForOpenScience/django-elasticsearch-metrics.svg?branch=master)](https://travis-ci.org/CenterForOpenScience/django-elasticsearch-metrics)
54
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)
65

76
Django app for storing time-series metrics in Elasticsearch.

docker-compose.yml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@ version: '3'
22
services:
33
elasticsearch:
44
image: docker.elastic.co/elasticsearch/elasticsearch:6.3.1
5-
ports:
6-
# Prevent clashing with another running cluster
7-
- 9201:9200
5+
testbox:
6+
build:
7+
context: .
8+
dockerfile: testbox.Dockerfile
9+
depends_on:
10+
- elasticsearch
11+
environment:
12+
TOXENV: py39-django40
13+
ELASTICSEARCH_HOST: elasticsearch:9200
14+
volumes:
15+
- ./:/code:cached

elasticsearch_metrics/__init__.py

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1 @@
1-
from django.apps import AppConfig
2-
from django.conf import settings
3-
from elasticsearch_dsl.connections import connections
4-
from django.utils.module_loading import autodiscover_modules
5-
6-
__version__ = "5.0.0"
7-
8-
default_app_config = "elasticsearch_metrics.ElasticsearchMetricsConfig"
9-
10-
11-
class ElasticsearchMetricsConfig(AppConfig):
12-
name = "elasticsearch_metrics"
13-
14-
def ready(self):
15-
connections.configure(**settings.ELASTICSEARCH_DSL)
16-
autodiscover_modules("metrics")
1+
__version__ = "2022.0.5"

elasticsearch_metrics/apps.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from django.apps import AppConfig
2+
from django.conf import settings
3+
from elasticsearch_dsl.connections import connections
4+
from django.utils.module_loading import autodiscover_modules
5+
6+
7+
class ElasticsearchMetricsConfig(AppConfig):
8+
name = "elasticsearch_metrics"
9+
10+
def ready(self):
11+
connections.configure(**settings.ELASTICSEARCH_DSL)
12+
autodiscover_modules("metrics")

elasticsearch_metrics/metrics.py

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
from collections import ChainMap
2+
import logging
3+
14
from django.apps import apps
25
from django.conf import settings
36
from django.utils import timezone
4-
from django.utils.six import add_metaclass
57
from elasticsearch.exceptions import NotFoundError
6-
from elasticsearch_dsl import Document, connections, Index
8+
from elasticsearch_dsl import Document, connections
79
from elasticsearch_dsl.document import IndexMeta, MetaField
8-
from elasticsearch_dsl.index import DEFAULT_INDEX
10+
from elasticsearch_dsl.index import Index
911

1012
from elasticsearch_metrics import signals
1113
from elasticsearch_metrics import exceptions
@@ -17,6 +19,22 @@
1719

1820
DEFAULT_DATE_FORMAT = "%Y.%m.%d"
1921

22+
logger = logging.getLogger(__name__)
23+
24+
25+
class ReadonlyAttrMap:
26+
def __init__(self, inner_obj):
27+
self.__inner_obj = inner_obj
28+
29+
def __getitem__(self, key):
30+
try:
31+
return getattr(self.__inner_obj, key)
32+
except AttributeError as e:
33+
raise KeyError(key) from e
34+
35+
def __contains__(self, key):
36+
return hasattr(self.__inner_obj, key)
37+
2038

2139
class MetricMeta(IndexMeta):
2240
"""Metaclass for the base `Metric` class."""
@@ -71,41 +89,31 @@ def __new__(mcls, name, bases, attrs): # noqa: B902
7189

7290
# Override IndexMeta.construct_index so that
7391
# a new Index is created for every metric class
92+
# and Index attrs are inherited
7493
@classmethod
7594
def construct_index(cls, opts, bases):
76-
i = None
77-
if opts is None:
78-
# Inherit Index from base classes
79-
for b in bases:
80-
if getattr(b, "_index", DEFAULT_INDEX) is not DEFAULT_INDEX:
81-
parent_index = b._index
82-
i = Index(
83-
parent_index._name,
84-
doc_type=parent_index._mapping.doc_type,
85-
using=parent_index._using,
86-
)
87-
i._settings = parent_index._settings.copy()
88-
i._aliases = parent_index._aliases.copy()
89-
i._analysis = parent_index._analysis.copy()
90-
i._doc_types = parent_index._doc_types[:]
91-
break
92-
if i is None:
93-
i = Index(
94-
getattr(opts, "name", "*"),
95-
doc_type=getattr(opts, "doc_type", "doc"),
96-
using=getattr(opts, "using", "default"),
97-
)
98-
i.settings(**getattr(opts, "settings", {}))
99-
i.aliases(**getattr(opts, "aliases", {}))
100-
for a in getattr(opts, "analyzers", ()):
95+
parent_configs = [
96+
base._index.to_dict() for base in bases if hasattr(base, "_index")
97+
]
98+
if opts:
99+
index_config = ChainMap(ReadonlyAttrMap(opts), *parent_configs)
100+
else:
101+
index_config = ChainMap(*parent_configs)
102+
103+
i = Index(
104+
index_config.get("name", "*"),
105+
using=index_config.get("using", "default"),
106+
)
107+
i.settings(**index_config.get("settings", {}))
108+
i.aliases(**index_config.get("aliases", {}))
109+
for a in index_config.get("analyzers", ()):
101110
i.analyzer(a)
102111
return i
103112

104113

105114
# We need this intermediate BaseMetric class so that
106115
# we can run MetricMeta ahead of IndexMeta
107-
@add_metaclass(MetricMeta)
108-
class BaseMetric(object):
116+
class BaseMetric(metaclass=MetricMeta):
109117
"""Base metric class with which to define custom metric classes.
110118
111119
Example usage:
@@ -114,6 +122,7 @@ class BaseMetric(object):
114122
115123
from elasticsearch_metrics import metrics
116124
125+
117126
class PageView(metrics.Metric):
118127
user_id = metrics.Integer(index=True, doc_values=True)
119128
@@ -162,7 +171,7 @@ def check_index_template(cls, using=None):
162171
raise exceptions.IndexTemplateNotFoundError(
163172
"{template_name} does not exist for {metric_name}".format(**locals()),
164173
client_error=client_error,
165-
)
174+
) from client_error
166175
else:
167176
current_data = list(template.values())[0]
168177
template_data = cls.get_index_template().to_dict()

elasticsearch_metrics/registry.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ def get_metric(self, app_label, metric_name=None):
4848
app_metrics = self._get_metrics_for_app(app_label=app_label)
4949
try:
5050
return app_metrics[metric_name.lower()]
51-
except KeyError:
51+
except KeyError as e:
5252
raise LookupError(
5353
"App '{}' doesn't have a '{}' metric.".format(app_label, metric_name)
54-
)
54+
) from e
5555

5656
def get_metrics(self, app_label=None):
5757
"""Return list of registered metric classes, optionally filtered on an app_label."""

elasticsearch_metrics/signals.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
from django.dispatch import Signal
22

33
# Sent before index template is created in elasticsearch
4-
pre_index_template_create = Signal(providing_args=["index_template", "using"])
4+
pre_index_template_create = Signal()
55
# Sent after index template is created in elasticsearch
6-
post_index_template_create = Signal(providing_args=["index_template", "using"])
6+
post_index_template_create = Signal()
77
# Sent at the beginning of Metric's save()
8-
pre_save = Signal(providing_args=["instance", "using", "index"])
8+
pre_save = Signal()
99
# Like pre_save, but sent at the end of the save() method
10-
post_save = Signal(providing_args=["instance", "using", "index"])
10+
post_save = Signal()

0 commit comments

Comments
 (0)