Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore python upgrade #75

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.6.8
3.11.10
8 changes: 4 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
language: python
python:
- "3.6.5"
- "3.11.10"
# command to install dependencies
install: "pip install -r requirements-dev.txt"
install: "make dev"
# command to run tests
script:
- flake8 email_parser tests
- nosetests --with-coverage --cover-inclusive --cover-erase --cover-package=email_parser --cover-min-percentage=70
- make test
- make coverage
14 changes: 7 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
PYTHON=venv/bin/python3
PIP=venv/bin/pip
EI=venv/bin/easy_install
NOSE=venv/bin/nosetests
TEST_RUNNER=venv/bin/pytest
FLAKE=venv/bin/flake8
EMAILS_TEMPLATES_URI=git@github.com:KeepSafe/emails.git
EMAILS_PATH=emails
GUI_BIN=ks-email-parser
FLAGS=--with-coverage --cover-inclusive --cover-erase --cover-package=email_parser --cover-min-percentage=70
TEST_RUNNER_FLAGS=-s --durations=3 --durations-min=0.005
COVERAGE=venv/bin/coverage
PYPICLOUD_HOST=pypicloud.getkeepsafe.local
TWINE=./venv/bin/twine

Expand Down Expand Up @@ -39,17 +40,16 @@ flake:
$(FLAKE) email_parser tests

test: flake
$(NOSE) -s $(FLAGS)
$(COVERAGE) run -m pytest $(TEST_RUNNER_FLAGS)

vtest:
$(NOSE) -s -v $(FLAGS)
$(COVERAGE) run -m pytest -v $(TEST_RUNNER_FLAGS)

testloop:
while sleep 1; do $(NOSE) -s $(FLAGS); done
while sleep 1; do $(TEST_RUNNER) -s --lf $(TEST_RUNNER_FLAGS); done

cov cover coverage:
$(NOSE) -s --with-cover --cover-html --cover-html-dir ./coverage $(FLAGS)
echo "open file://`pwd`/coverage/index.html"
$(COVERAGE) report -m

clean:
rm -rf `find . -name __pycache__`
Expand Down
2 changes: 1 addition & 1 deletion email_parser/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def get_email_components(self, email_name, locale):
def get_email_variants(self, email_name):
email = fs.email(self.root_path, email_name, const.DEFAULT_LOCALE)
_, placeholders = reader.read(self.root_path, email)
variants = set([name for _, p in placeholders.items() for name in p.variants.keys()])
variants = {name for _, p in placeholders.items() for name in p.variants.keys()}
return list(variants)

def delete_email(self, email_name):
Expand Down
4 changes: 2 additions & 2 deletions email_parser/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class ProgressConsoleHandler(logging.StreamHandler):
def __init__(self, err_queue, warn_queue, *args, **kwargs):
self.err_msgs_queue = err_queue
self.warn_msgs_queue = warn_queue
super(ProgressConsoleHandler, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)

def _store_msg(self, msg, loglevel):
if loglevel == logging.ERROR:
Expand Down Expand Up @@ -60,7 +60,7 @@ def _flush_store(self, stream, msgs, header):
stream.write(header)
stream.write(self.terminator)
for idx, msg in enumerate(msgs):
stream.write('%s. %s' % (idx + 1, msg))
stream.write(f'{idx + 1}. {msg}')
stream.write(self.terminator)

def _flush_errors(self, stream):
Expand Down
4 changes: 2 additions & 2 deletions email_parser/fs.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ def _parse_params(pattern):
params = [p for p in map(lambda e: e[1], Formatter().parse(pattern)) if p]
if 'name' not in params:
raise MissingPatternParamError(
'{{name}} is a required parameter in the pattern but it is not present in {}'.format(pattern))
f'{{{{name}}}} is a required parameter in the pattern but it is not present in {pattern}')
if 'locale' not in params:
raise MissingPatternParamError(
'{{locale}} is a required parameter in the pattern but it is not present in {}'.format(pattern))
f'{{{{locale}}}} is a required parameter in the pattern but it is not present in {pattern}')
return params


Expand Down
4 changes: 2 additions & 2 deletions email_parser/link_shortener.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import requests


class NullShortener(object):
class NullShortener:
name = 'null'

def __init__(self, config):
Expand All @@ -11,7 +11,7 @@ def shorten(self, link):
return link


class KsShortener(object):
class KsShortener:
name = 'keepsafe'
url = 'http://4uon.ly/url/'

Expand Down
10 changes: 5 additions & 5 deletions email_parser/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ def __iter__(self):
if self._opt_attr:
attributes.update(self._opt_attr)
for k, v in attributes.items():
if k is 'type':
if k == 'type':
yield k, self.type.value
elif k is '_content':
elif k == '_content':
yield 'content', v
elif k is '_opt_attr':
elif k == '_opt_attr':
continue
else:
yield k, v
Expand Down Expand Up @@ -106,10 +106,10 @@ def get_content(self, variant=None):
div_style = "vertical-align: middle;text-align: center;"
constraints = ""
if self.alt:
optional += " alt=\"{}\"".format(self.alt)
optional += f" alt=\"{self.alt}\""
for style_tag in ['max-width', 'max-height']:
if style_tag in mapping:
constraints += "{}: {};".format(style_tag, mapping[style_tag])
constraints += f"{style_tag}: {mapping[style_tag]};"
mapping.update({
'style': div_style + constraints,
'id': self.id,
Expand Down
10 changes: 5 additions & 5 deletions email_parser/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def parse_placeholder(placeholder_str):
attr_name, attr_value = attr_str.split('=')
args[attr_name] = attr_value
except ValueError:
ValueError('Malformed attributes definition: %s'.format(args_str))
ValueError(f'Malformed attributes definition: {args_str}')
return MetaPlaceholder(name, placeholder_type, args)


Expand All @@ -43,7 +43,7 @@ def _placeholders(tree, prefix=''):
is_global = (prefix == const.GLOBALS_PLACEHOLDER_PREFIX)
result = OrderedDict()
for element in tree.xpath('./string | ./string-array | bitmap | ./array'):
name = '{0}{1}'.format(prefix, element.get('name'))
name = '{}{}'.format(prefix, element.get('name'))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: f-string

placeholder_type = PlaceholderType[element.get('type', PlaceholderType.text.value)]
opt_attrs = dict(element.items())
del opt_attrs['name']
Expand Down Expand Up @@ -176,12 +176,12 @@ def _read_xml(path):
def _read_xml_from_content(content):
if not content:
return None
parser = etree.XMLParser(encoding='utf-8')
try:
parser = etree.XMLParser(encoding='utf-8')
root = etree.fromstring(content.encode('utf-8'), parser=parser)
return etree.ElementTree(root)
except etree.ParseError as e:
logger.exception('Unable to parse XML content %s %s', content, e)
except (etree.ParseError, etree.XMLSyntaxError):
logger.error('Unable to parse XML content %s', content)
return None
except TypeError:
# got None? no results
Expand Down
16 changes: 8 additions & 8 deletions email_parser/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ def _md_to_html(text, base_url=None):

def _split_subject(placeholders):
return (placeholders.get(const.SUBJECT_PLACEHOLDER),
dict((k, v) for k, v in placeholders.items() if k != const.SUBJECT_PLACEHOLDER))
{k: v for k, v in placeholders.items() if k != const.SUBJECT_PLACEHOLDER})


def _transform_extended_tags(content):
regex = r"{{(.*):(.*):(.*)}}"
return re.sub(regex, lambda match: '{{%s}}' % match.group(2), content)


class HtmlRenderer(object):
class HtmlRenderer:
"""
Renders email' body as html.
"""
Expand Down Expand Up @@ -107,7 +107,7 @@ def _concat_parts(self, subject, parts, variant):
content = _transform_extended_tags(self.template.content)
return renderer.render(content, placeholders)
except pystache.context.KeyNotFoundError as e:
message = 'template %s for locale %s has missing placeholders: %s' % (self.template.name, self.locale, e)
message = f'template {self.template.name} for locale {self.locale} has missing placeholders: {e}'
raise MissingTemplatePlaceholderError(message) from e

def render(self, placeholders, variant=None, highlight=None):
Expand All @@ -118,7 +118,7 @@ def render(self, placeholders, variant=None, highlight=None):
return html


class TextRenderer(object):
class TextRenderer:
"""
Renders email's body as text.
"""
Expand All @@ -138,7 +138,7 @@ def _html_to_text(self, html):
href = anchor.get('href') or text
# href = self.shortener.shorten(href)
if href != text:
anchor.replace_with('{} ({})'.format(text, href))
anchor.replace_with(f'{text} ({href})')
elif href:
anchor.replace_with(href)

Expand All @@ -151,7 +151,7 @@ def _html_to_text(self, html):
ordered_lists = soup('ol')
for ordered_list in ordered_lists:
for idx, element in enumerate(ordered_list('li')):
element.replace_with('%s. %s' % (idx + 1, element.string))
element.replace_with(f'{idx + 1}. {element.string}')

return soup.get_text()

Expand All @@ -167,7 +167,7 @@ def render(self, placeholders, variant=None):
return const.TEXT_EMAIL_PLACEHOLDER_SEPARATOR.join(v for v in filter(bool, parts))


class SubjectRenderer(object):
class SubjectRenderer:
"""
Renders email's subject as text.
"""
Expand All @@ -190,7 +190,7 @@ def render(email_locale, template, placeholders, variant=None, highlight=None):
try:
html = html_renderer.render(placeholders, variant, highlight)
except MissingTemplatePlaceholderError as e:
message = 'failed to generate html content for locale: {} with message: {}'.format(email_locale, e)
message = f'failed to generate html content for locale: {email_locale} with message: {e}'
raise RenderingError(message) from e

return subject, text, html
12 changes: 9 additions & 3 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
[metadata]
description-file = README.md
description_file = README.md

[flake8]
max-line-length = 120
max_line_length = 120
ignore = F403, F405, F401

[pep8]
max-line-length = 120
max_line_length = 120

[coverage:run]
branch = True

[coverage:report]
fail_under = 85
11 changes: 6 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import os
from setuptools import setup, find_packages

version = '0.3.2'
version = '1.0.0'

install_requires = [
'Markdown < 3',
'beautifulsoup4 < 5',
'inlinestyler==0.2.1',
'pystache < 0.6',
'pystache < 0.7',
'lxml < 5',
'parse < 2'
]

tests_require = [
'nose',
'flake8==2.5.4',
'coverage',
'pytest >= 8',
'coverage >= 7',
'flake8 < 4',
]

devtools_require = [
Expand Down
12 changes: 11 additions & 1 deletion tests/test_fs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from email_parser.model import *


class MockPath(object):
class MockPath:
def __init__(self, path, is_dir=False, parent='.'):
self.path = path
self._is_dir = is_dir
Expand All @@ -29,6 +29,16 @@ def __str__(self):
return self.path


class TestUtilities(TestCase):
def test__parse_params_exceptions(self):
with self.assertRaises(MissingPatternParamError):
fs._parse_params('test')
with self.assertRaises(MissingPatternParamError):
fs._parse_params('test {name}')
with self.assertRaises(MissingPatternParamError):
fs._parse_params('test {locale}')


class TestFs(TestCase):
def setUp(self):
self.patch_path = patch('email_parser.fs.Path')
Expand Down
5 changes: 3 additions & 2 deletions tests/test_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@ def test_rtl_locale(self):
placeholders = {'content': Placeholder('content', 'dummy_content')}

actual = r.render(placeholders)
self.assertEqual('<body dir="rtl">\n <p>\n dummy_content\n </p>\n</body>', actual)
expected = '<body dir="rtl">\n <p>\n dummy_content\n </p>\n</body>\n'
self.assertEqual(expected, actual)

def test_rtl_two_placeholders(self):
email_locale = 'ar'
Expand All @@ -214,7 +215,7 @@ def test_rtl_two_placeholders(self):

actual = r.render(placeholders)
expected = '<body dir="rtl">\n <div>\n <p>\n dummy_content1\n </p>\n </div>\n <div>\n <p>\n dummy_content2\n </p>\n </div>\
\n</body>'
\n</body>\n'

self.assertEqual(expected, actual)

Expand Down