Skip to content

Commit

Permalink
Merge pull request #510 from kalaspuff/feature/added-py37-dev-tests
Browse files Browse the repository at this point in the history
CI testing for Python 3.7 support
  • Loading branch information
kalaspuff authored Jul 2, 2018
2 parents a9f6cbf + c6c0915 commit c72b4b5
Show file tree
Hide file tree
Showing 22 changed files with 45 additions and 71 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ python:
- 3.6.3
- 3.6.4
- 3.6.5
- 3.6.6
- 3.6-dev # 3.6 development branch
- 3.7-dev # 3.7 development branch
services:
- rabbitmq
install:
Expand Down
3 changes: 1 addition & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,13 @@ install:
pip install -U .

development:
pip install -r requirements-dev.txt
pip install -r requirements.txt

uninstall:
pip uninstall -y tomodachi

lint:
pycodestyle --ignore E501 --exclude proto_build .
pycodestyle --ignore E501 --exclude proto_build,build .
mypy ./
@echo "ok"

Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ Nothing more nothing less. It's actually as easy as that.

Requirements 👍
---------------
* Python_ 3.5.3+, 3.6+
* Python_ 3.5.3+, 3.6+, 3.7+
* aiohttp_
* aiobotocore_
* aioamqp_
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pycparser==2.18
aioamqp==0.11.0
aiobotocore==0.9.2
aiodns==1.1.1
Expand All @@ -18,7 +19,7 @@ mypy==0.610
packaging==17.1
protobuf==3.6.0
pycares==2.3.0
pycodestyle==2.3.1
pycodestyle==2.4.0
py==1.5.4
pyparsing==2.2.0
pytest==3.6.2
Expand Down
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import tomodachi.__version__

install_requires = [
'pycparser>=2.18',
'aioamqp>=0.10.0, <0.12.0',
'ujson>=1.35',
'uvloop>=0.8.1',
Expand Down Expand Up @@ -32,6 +33,7 @@ def read(f: str) -> str:
'Programming Language :: Python :: 3 :: Only',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'License :: OSI Approved :: MIT License',
'Development Status :: 2 - Pre-Alpha',
'Topic :: Software Development :: Libraries :: Application Frameworks',
Expand Down
6 changes: 3 additions & 3 deletions tests/run_test_service_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@
from tomodachi.importer import ServiceImporter


def start_service(filename: str, monkeypatch: Any=None) -> Tuple:
def start_service(filename: str, monkeypatch: Any = None) -> Tuple:
if monkeypatch:
monkeypatch.setattr(logging.root, 'handlers', [])

loop = asyncio.get_event_loop() # type: Any
service = None # type: Any

def stop_services(loop: Any=None) -> None:
def stop_services(loop: Any = None) -> None:
if not loop:
loop = asyncio.get_event_loop()
asyncio.ensure_future(_stop_services())

def force_stop_services(loop: Any=None) -> None:
def force_stop_services(loop: Any = None) -> None:
if not loop:
loop = asyncio.get_event_loop()
asyncio.ensure_future(_force_stop_services())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def check_closer(self):
self.closer.set_result(None)

@amqp('test.raw.topic')
async def test(self, value: Any, default_value: bool=True) -> None:
async def test(self, value: Any, default_value: bool = True) -> None:
if value == self.data_uuid:
self.test_topic_data_received = default_value
self.test_topic_data = value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def check_closer(self):
self.closer.set_result(None)

@aws_sns_sqs('test-raw-topic')
async def test(self, value: Any, default_value: bool=True) -> None:
async def test(self, value: Any, default_value: bool = True) -> None:
if value == self.data_uuid:
self.test_topic_data_received = default_value
self.test_topic_data = value
Expand Down
2 changes: 1 addition & 1 deletion tests/services/mock_decorator_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ class MockDecoratorService(tomodachi.Service):
function_tested = False

@aws_sns_sqs('test-topic')
async def test(self, default_value: bool=True) -> None:
async def test(self, default_value: bool = True) -> None:
self.function_tested = default_value
32 changes: 0 additions & 32 deletions tests/test_non_packaged_imported_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,56 +67,24 @@ def test_sub_service(monkeypatch: Any, capsys: Any, loop: Any) -> None:
services, future = start_service('tests/services/test/service.py', monkeypatch)


@pytest.mark.skipif(sys.version_info < (3, 7, 0), reason="Test executes only on Python 3.7")
def test_sub_service_py37(monkeypatch: Any, capsys: Any, loop: Any) -> None:
services, future = start_service('tests/services/test/service.py', monkeypatch)

os.kill(os.getpid(), signal.SIGINT)
loop.run_until_complete(future)


@pytest.mark.skipif(sys.version_info >= (3, 7, 0), reason="Test skipped on Python 3.7")
def test_sub_service_without_py_ending(monkeypatch: Any, capsys: Any, loop: Any) -> None:
with pytest.raises(tomodachi.importer.ServicePackageError):
services, future = start_service('tests/services/test/service', monkeypatch)


@pytest.mark.skipif(sys.version_info < (3, 7, 0), reason="Test executes only on Python 3.7")
def test_sub_service_without_py_ending_py37(monkeypatch: Any, capsys: Any, loop: Any) -> None:
services, future = start_service('tests/services/test/service', monkeypatch)

os.kill(os.getpid(), signal.SIGINT)
loop.run_until_complete(future)


@pytest.mark.skipif(sys.version_info >= (3, 7, 0), reason="Test skipped on Python 3.7")
def test_same_named_sub_service(monkeypatch: Any, capsys: Any, loop: Any) -> None:
with pytest.raises(tomodachi.importer.ServicePackageError):
services, future = start_service('tests/services/test/test.py', monkeypatch)


@pytest.mark.skipif(sys.version_info < (3, 7, 0), reason="Test executes only on Python 3.7")
def test_same_named_sub_service_py37(monkeypatch: Any, capsys: Any, loop: Any) -> None:
services, future = start_service('tests/services/test/test.py', monkeypatch)

os.kill(os.getpid(), signal.SIGINT)
loop.run_until_complete(future)


@pytest.mark.skipif(sys.version_info >= (3, 7, 0), reason="Test skipped on Python 3.7")
def test_same_named_sub_service_without_py_ending(monkeypatch: Any, capsys: Any, loop: Any) -> None:
with pytest.raises(tomodachi.importer.ServicePackageError):
services, future = start_service('tests/services/test/test', monkeypatch)


@pytest.mark.skipif(sys.version_info < (3, 7, 0), reason="Test executes only on Python 3.7")
def test_same_named_sub_service_without_py_ending_py37(monkeypatch: Any, capsys: Any, loop: Any) -> None:
services, future = start_service('tests/services/test/test', monkeypatch)

os.kill(os.getpid(), signal.SIGINT)
loop.run_until_complete(future)


def test_sub_service_with_reserved_name(monkeypatch: Any, capsys: Any, loop: Any) -> None:
with pytest.raises(tomodachi.importer.ServicePackageError):
services, future = start_service('tests/services/os/os.py', monkeypatch)
Expand Down
2 changes: 1 addition & 1 deletion tomodachi/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def main(self, argv: List[str]) -> None:
self.help_command()


def cli_entrypoint(argv: Optional[List[str]]=None) -> None:
def cli_entrypoint(argv: Optional[List[str]] = None) -> None:
if argv is None:
argv = sys.argv
CLI().main(argv[1:])
2 changes: 1 addition & 1 deletion tomodachi/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@


class ServiceContainer(object):
def __init__(self, module_import: ModuleType, configuration: Optional[Dict]=None) -> None:
def __init__(self, module_import: ModuleType, configuration: Optional[Dict] = None) -> None:
self.module_import = module_import

self.file_path = module_import.__file__
Expand Down
2 changes: 1 addition & 1 deletion tomodachi/helpers/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from typing import Optional, Union, Any


def log_setup(service: Any, name: Optional[str]=None, level: Optional[Union[str, int]]=None, formatter: Optional[Union[logging.Formatter, str, bool]]=True, filename: Optional[str]=None) -> logging.Logger:
def log_setup(service: Any, name: Optional[str] = None, level: Optional[Union[str, int]] = None, formatter: Optional[Union[logging.Formatter, str, bool]] = True, filename: Optional[str] = None) -> logging.Logger:
if not name:
name = 'log.{}'.format(service.name)
if not filename:
Expand Down
4 changes: 3 additions & 1 deletion tomodachi/importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ def import_service_file(cls, file_name: str) -> ModuleType:
except ModuleNotFoundError as e: # noqa
file_path_package_name = file_path[:-3] if file_path.endswith('.py') else file_path
if str(e) == "__path__ attribute not found on '{}' while trying to find '{}'".format(file_path_package_name.rsplit('/', 2)[1], '.'.join(file_path_package_name.rsplit('/', 2)[1:])):
# ModuleNotFoundError: __path__ attribute not found on 'os' while trying to find 'os.code'
logging.getLogger('import').warning('Invalid service package/parent name, may conflict with Python internals: "{}" - change parent folder name'.format(file_path.rsplit('/', 2)[1]))
raise ServicePackageError from e
if str(e) == "__path__ attribute not found on '{}'while trying to find '{}'".format(file_path_package_name.rsplit('/', 2)[1], '.'.join(file_path_package_name.rsplit('/', 2)[1:])):
logging.getLogger('import').warning('Invalid service package/parent name, may conflict with Python internals: "{}" - change parent folder name'.format(file_path.rsplit('/', 2)[1]))
raise ServicePackageError from e
raise e
Expand Down
2 changes: 1 addition & 1 deletion tomodachi/invoker/decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def __repr__(self) -> str:
return '<function {} at {}>'.format(self.__qualname__, hex(id(self)))


def decorator(include_function: Any=False) -> Callable:
def decorator(include_function: Any = False) -> Callable:
fn = None
if include_function and callable(include_function):
fn = include_function
Expand Down
2 changes: 1 addition & 1 deletion tomodachi/launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class ServiceLauncher(object):
services = set() # type: set

@classmethod
def run_until_complete(cls, service_files: Union[List, set], configuration: Optional[Dict]=None, watcher: Optional[tomodachi.watcher.Watcher]=None) -> None:
def run_until_complete(cls, service_files: Union[List, set], configuration: Optional[Dict] = None, watcher: Optional[tomodachi.watcher.Watcher] = None) -> None:
def stop_services() -> None:
asyncio.ensure_future(_stop_services())

Expand Down
10 changes: 5 additions & 5 deletions tomodachi/transport/amqp.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class AmqpTransport(Invoker):
transport = None # type: Any

@classmethod
async def publish(cls, service: Any, data: Any, routing_key: str='', exchange_name: str='', wait: bool=True) -> None:
async def publish(cls, service: Any, data: Any, routing_key: str = '', exchange_name: str = '', wait: bool = True) -> None:
if not cls.channel:
await cls.connect(cls, service, service.context)
exchange_name = exchange_name or cls.exchange_name or 'amq.topic'
Expand Down Expand Up @@ -114,7 +114,7 @@ def prefix_queue_name(cls, queue_name: str, context: Dict) -> str:
return '{}{}'.format(context.get('options', {}).get('amqp', {}).get('queue_name_prefix'), queue_name)
return queue_name

async def subscribe_handler(cls: Any, obj: Any, context: Dict, func: Any, routing_key: str, callback_kwargs: Optional[Union[list, set, tuple]]=None, exchange_name: str='', competing: Optional[bool]=None, queue_name: Optional[str]=None) -> Any:
async def subscribe_handler(cls: Any, obj: Any, context: Dict, func: Any, routing_key: str, callback_kwargs: Optional[Union[list, set, tuple]] = None, exchange_name: str = '', competing: Optional[bool] = None, queue_name: Optional[str] = None) -> Any:
async def handler(payload: Any, delivery_tag: Any) -> Any:
_callback_kwargs = callback_kwargs # type: Any
values = inspect.getfullargspec(func)
Expand Down Expand Up @@ -264,9 +264,9 @@ async def subscribe(cls: Any, obj: Any, context: Dict) -> Optional[Callable]:
channel = await cls.connect(cls, obj, context)

async def _subscribe() -> None:
async def declare_queue(routing_key: str, func: Callable, exchange_name: str='', exchange_type: str='topic', queue_name: Optional[str]=None,
passive: bool=False, durable: bool=True, exclusive: bool=False, auto_delete: bool=False,
competing_consumer: Optional[bool]=None) -> Optional[str]:
async def declare_queue(routing_key: str, func: Callable, exchange_name: str = '', exchange_type: str = 'topic', queue_name: Optional[str] = None,
passive: bool = False, durable: bool = True, exclusive: bool = False, auto_delete: bool = False,
competing_consumer: Optional[bool] = None) -> Optional[str]:
try:
if exchange_name and exchange_name != 'amq.topic':
await channel.exchange_declare(exchange_name=exchange_name, type_name=exchange_type, passive=False, durable=True, auto_delete=False)
Expand Down
10 changes: 5 additions & 5 deletions tomodachi/transport/aws_sns_sqs.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class AWSSNSSQSTransport(Invoker):
close_waiter = None

@classmethod
async def publish(cls, service: Any, data: Any, topic: str, wait: bool=True) -> None:
async def publish(cls, service: Any, data: Any, topic: str, wait: bool = True) -> None:
message_protocol = getattr(service, 'message_protocol', None)

payload = data
Expand Down Expand Up @@ -101,7 +101,7 @@ def prefix_queue_name(cls, queue_name: str, context: Dict) -> str:
return '{}{}'.format(context.get('options', {}).get('aws_sns_sqs', {}).get('queue_name_prefix'), queue_name)
return queue_name

async def subscribe_handler(cls: Any, obj: Any, context: Dict, func: Any, topic: str, callback_kwargs: Optional[Union[list, set, tuple]]=None, competing: Optional[bool]=None, queue_name: Optional[str]=None, **kwargs: Any) -> Any:
async def subscribe_handler(cls: Any, obj: Any, context: Dict, func: Any, topic: str, callback_kwargs: Optional[Union[list, set, tuple]] = None, competing: Optional[bool] = None, queue_name: Optional[str] = None, **kwargs: Any) -> Any:
parser_kwargs = kwargs
message_protocol = context.get('message_protocol')
# Validate the parser kwargs if there is a validation function in the
Expand All @@ -111,7 +111,7 @@ async def subscribe_handler(cls: Any, obj: Any, context: Dict, func: Any, topic:
if protocol_kwargs_validation_func:
protocol_kwargs_validation_func(**parser_kwargs)

async def handler(payload: Optional[str], receipt_handle: Optional[str]=None, queue_url: Optional[str]=None) -> Any:
async def handler(payload: Optional[str], receipt_handle: Optional[str] = None, queue_url: Optional[str] = None) -> Any:
if not payload or payload == DRAIN_MESSAGE_PAYLOAD:
await cls.delete_message(cls, receipt_handle, queue_url, context)
return
Expand Down Expand Up @@ -431,7 +431,7 @@ async def subscribe_wildcard_topic(cls: Any, topic: str, queue_arn: str, queue_u

return None

async def subscribe_topics(cls: Any, topic_arn_list: List, queue_arn: str, queue_url: str, context: Dict, queue_policy: Optional[Dict]=None) -> List:
async def subscribe_topics(cls: Any, topic_arn_list: List, queue_arn: str, queue_url: str, context: Dict, queue_policy: Optional[Dict] = None) -> List:
if not cls.clients or not cls.clients.get('sns'):
cls.create_client(cls, 'sns', context)
client = cls.clients.get('sns')
Expand Down Expand Up @@ -646,7 +646,7 @@ async def subscribe(cls: Any, obj: Any, context: Dict) -> Optional[Callable]:
async def _subscribe() -> None:
cls.close_waiter = asyncio.Future()

async def setup_queue(topic: str, func: Callable, queue_name: Optional[str]=None, competing_consumer: Optional[bool]=None) -> str:
async def setup_queue(topic: str, func: Callable, queue_name: Optional[str] = None, competing_consumer: Optional[bool] = None) -> str:
_uuid = obj.uuid

if queue_name and competing_consumer is False:
Expand Down
10 changes: 5 additions & 5 deletions tomodachi/transport/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)

@staticmethod
def colorize_status(text: Optional[Union[str, int]], status: Optional[Union[str, int, bool]]=False) -> str:
def colorize_status(text: Optional[Union[str, int]], status: Optional[Union[str, int, bool]] = False) -> str:
if status is False:
status = text
status_code = str(status) if status else None
Expand All @@ -56,7 +56,7 @@ def colorize_status(text: Optional[Union[str, int]], status: Optional[Union[str,

return str(text) if text else ''

def handle_error(self, request: Any, status: int=500, exc: Any=None, message: Optional[str]=None) -> web.Response:
def handle_error(self, request: Any, status: int = 500, exc: Any = None, message: Optional[str] = None) -> web.Response:
"""Handle errors.
Returns HTTP response with specific status code. Logs additional
Expand Down Expand Up @@ -123,15 +123,15 @@ def __call__(self) -> RequestHandler:


class DynamicResource(web_urldispatcher.DynamicResource): # type: ignore
def __init__(self, pattern: Any, *, name: Optional[str]=None) -> None:
def __init__(self, pattern: Any, *, name: Optional[str] = None) -> None:
self._routes = [] # type: List
self._name = name
self._pattern = pattern
self._formatter = ''


class Response(object):
def __init__(self, *, body: Optional[str]=None, status: int=200, reason: Optional[str]=None, headers: Optional[Union[Dict, CIMultiDict, CIMultiDictProxy]]=None, content_type: Optional[str]=None, charset: Optional[str]=None) -> None:
def __init__(self, *, body: Optional[str] = None, status: int = 200, reason: Optional[str] = None, headers: Optional[Union[Dict, CIMultiDict, CIMultiDictProxy]] = None, content_type: Optional[str] = None, charset: Optional[str] = None) -> None:
if headers is None:
headers = CIMultiDict()
elif not isinstance(headers, (CIMultiDict, CIMultiDictProxy)):
Expand All @@ -146,7 +146,7 @@ def __init__(self, *, body: Optional[str]=None, status: int=200, reason: Optiona

self.missing_content_type = hdrs.CONTENT_TYPE not in headers and not content_type and not charset

def get_aiohttp_response(self, context: Dict, default_charset: Optional[str]=None, default_content_type: Optional[str]=None) -> web.Response:
def get_aiohttp_response(self, context: Dict, default_charset: Optional[str] = None, default_content_type: Optional[str] = None) -> web.Response:
if self.missing_content_type:
self.charset = default_charset
self.content_type = default_content_type
Expand Down
Loading

0 comments on commit c72b4b5

Please sign in to comment.