Skip to content

Commit

Permalink
Merge pull request #193 from reportportal/develop
Browse files Browse the repository at this point in the history
Release
  • Loading branch information
HardNorth committed Sep 15, 2022
2 parents 5713ad2 + b18692c commit cf52b15
Show file tree
Hide file tree
Showing 22 changed files with 376 additions and 152 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
- uses: actions/checkout@v2

- name: Generate versions
uses: HardNorth/github-version-generate@v1.1.2
uses: HardNorth/github-version-generate@v1.2.0
with:
version-source: file
version-file: ${{ env.VERSION_FILE }}
Expand Down
10 changes: 9 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
name: Tests

on: [push, pull_request]
on:
push:
branches:
- '*'
- '!master'
pull_request:
branches:
- 'master'
- 'develop'

jobs:
build:
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# Changelog

## [Unreleased]
### Changed
- `LogManager` class moved from `core` package to `logs` package, by @HardNorth
### Fixed
- Issue [#192](https://github.com/reportportal/client-Python/issues/192): launch URL generation, by @HardNorth

## [5.2.3]
### Added
- Ability to pass client instance in `RPLogHandler` constructor, by @HardNorth
- Issue [#179](https://github.com/reportportal/client-Python/issues/179): batch logging request payload size tracking, by @HardNorth
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
[![Python versions](https://img.shields.io/pypi/pyversions/reportportal-client.svg)](https://pypi.org/project/reportportal-client)
[![Build Status](https://github.com/reportportal/client-Python/actions/workflows/tests.yml/badge.svg)](https://github.com/reportportal/client-Python/actions/workflows/tests.yml)
[![codecov.io](https://codecov.io/gh/reportportal/client-Python/branch/master/graph/badge.svg)](https://codecov.io/gh/reportportal/client-Python)
[![Join Slack chat!](https://reportportal-slack-auto.herokuapp.com/badge.svg)](https://reportportal-slack-auto.herokuapp.com)
[![stackoverflow](https://img.shields.io/badge/reportportal-stackoverflow-orange.svg?style=flat)](http://stackoverflow.com/questions/tagged/reportportal)
[![Build with Love](https://img.shields.io/badge/build%20with-❤%EF%B8%8F%E2%80%8D-lightgrey.svg)](http://reportportal.io?style=flat)

Library used only for implementors of custom listeners for ReportPortal

Expand Down
4 changes: 2 additions & 2 deletions reportportal_client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@

__all__ = [
'current',
"RPLogger",
"RPLogHandler",
'RPLogger',
'RPLogHandler',
'ReportPortalService',
'step',
]
32 changes: 25 additions & 7 deletions reportportal_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,10 @@
limitations under the License.
"""
import logging

import requests
from requests.adapters import HTTPAdapter, Retry

from ._local import set_current
from .core.log_manager import LogManager, MAX_LOG_BATCH_PAYLOAD_SIZE
from .core.rp_requests import (
HttpRequest,
ItemStartRequest,
Expand All @@ -29,6 +27,7 @@
LaunchFinishRequest
)
from .helpers import uri_join, verify_value_length
from .logs.log_manager import LogManager, MAX_LOG_BATCH_PAYLOAD_SIZE
from .static.defines import NOT_FOUND
from .steps import StepReporter

Expand Down Expand Up @@ -59,6 +58,7 @@ def __init__(self,
launch_id=None,
http_timeout=(10, 10),
log_batch_payload_size=MAX_LOG_BATCH_PAYLOAD_SIZE,
mode='DEFAULT',
**_):
"""Initialize required attributes.
Expand Down Expand Up @@ -100,6 +100,7 @@ def __init__(self,
self.session = requests.Session()
self.step_reporter = StepReporter(self)
self._item_stack = []
self.mode = mode
if retries:
retry_strategy = Retry(
total=retries,
Expand Down Expand Up @@ -244,10 +245,19 @@ def get_launch_ui_url(self):
:return: launch URL or all launches URL.
"""
ui_id = self.get_launch_ui_id()
launch_info = self.get_launch_info()
ui_id = launch_info.get('id') if launch_info else None
if not ui_id:
return
path = 'ui/#{0}/launches/all/{1}'.format(self.project, ui_id)
mode = launch_info.get('mode') if launch_info else None
if not mode:
mode = self.mode

launch_type = "launches" if mode.upper() == 'DEFAULT' else 'userdebug'

path = 'ui/#{project_name}/{launch_type}/all/{launch_id}'.format(
project_name=self.project.lower(), launch_type=launch_type,
launch_id=ui_id)
url = uri_join(self.endpoint, path)
logger.debug('get_launch_ui_url - ID: %s', self.launch_id)
return url
Expand Down Expand Up @@ -282,7 +292,6 @@ def start_launch(self,
start_time,
description=None,
attributes=None,
mode=None,
rerun=False,
rerun_of=None,
**kwargs):
Expand All @@ -292,12 +301,21 @@ def start_launch(self,
:param start_time: Launch start time
:param description: Launch description
:param attributes: Launch attributes
:param mode: Launch mode
:param rerun: Enables launch rerun mode
:param rerun_of: Rerun mode. Specifies launch to be re-runned.
Should be used with the 'rerun' option.
"""
url = uri_join(self.base_url_v2, 'launch')

# We are moving 'mode' param to the constructor, next code for the
# transition period only.
my_kwargs = dict(kwargs)
mode = my_kwargs.get('mode')
if not mode:
mode = self.mode
else:
del my_kwargs['mode']

request_payload = LaunchStartRequest(
name=name,
start_time=start_time,
Expand All @@ -306,7 +324,7 @@ def start_launch(self,
mode=mode,
rerun=rerun,
rerun_of=rerun_of or kwargs.get('rerunOf'),
**kwargs
**my_kwargs
).payload
response = HttpRequest(self.session.post,
url=url,
Expand Down
3 changes: 2 additions & 1 deletion reportportal_client/client.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ from typing import Any, Dict, List, Optional, Text, Tuple, Union

from requests import Session

from reportportal_client.core.log_manager import LogManager as LogManager
from reportportal_client.logs.log_manager import LogManager as LogManager
from reportportal_client.core.rp_issues import Issue as Issue
from reportportal_client.steps import StepReporter

Expand All @@ -27,6 +27,7 @@ class RPClient:
http_timeout: Union[float, Tuple[float, float]] = ...
session: Session = ...
step_reporter: StepReporter = ...
mode: str = ...

def __init__(
self,
Expand Down
6 changes: 6 additions & 0 deletions reportportal_client/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,9 @@
# limitations under the License

"""This package contains core reportportal-client modules."""

from reportportal_client.logs import log_manager

__all__ = [
'log_manager'
]
139 changes: 18 additions & 121 deletions reportportal_client/core/log_manager.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
"""This module contains management functionality for processing logs."""

# Copyright (c) 2022 EPAM Systems
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -13,127 +11,26 @@
# See the License for the specific language governing permissions and
# limitations under the License

import logging
from threading import Lock

from six.moves import queue

from reportportal_client import helpers
from reportportal_client.core.rp_requests import (
HttpRequest,
RPFile,
RPLogBatch,
RPRequestLog
)
from reportportal_client.core.worker import APIWorker
from reportportal_client.static.defines import NOT_FOUND

logger = logging.getLogger(__name__)

MAX_LOG_BATCH_PAYLOAD_SIZE = 65000000


class LogManager(object):
"""Manager of the log items."""
"""Deprecated module stub to avoid execution crashes.
def __init__(self, rp_url, session, api_version, launch_id, project_name,
max_entry_number=20, verify_ssl=True,
max_payload_size=MAX_LOG_BATCH_PAYLOAD_SIZE):
"""Initialize instance attributes.
.. deprecated:: 5.2.4
Use `logs.log_manager` instead.
"""

:param rp_url: Report portal URL
:param session: HTTP Session object
:param api_version: RP API version
:param launch_id: Parent launch UUID
:param project_name: RP project name
:param max_entry_number: The amount of log objects that need to be
gathered before processing
:param verify_ssl: Indicates that it is necessary to verify SSL
certificates within HTTP request
:param max_payload_size: maximum size in bytes of logs that can be
processed in one batch
"""
self._lock = Lock()
self._batch = []
self._payload_size = helpers.TYPICAL_MULTIPART_FOOTER_LENGTH
self._worker = None
self.api_version = api_version
self.queue = queue.PriorityQueue()
self.launch_id = launch_id
self.max_entry_number = max_entry_number
self.max_payload_size = max_payload_size
self.project_name = project_name
self.rp_url = rp_url
self.session = session
self.verify_ssl = verify_ssl
import warnings

self._log_endpoint = (
'{rp_url}/api/{version}/{project_name}/log'
.format(rp_url=rp_url, version=self.api_version,
project_name=self.project_name))
from reportportal_client.logs.log_manager import LogManager, \
MAX_LOG_BATCH_PAYLOAD_SIZE

def _send_batch(self):
"""Send existing batch logs to the worker."""
batch = RPLogBatch(self._batch)
http_request = HttpRequest(
self.session.post, self._log_endpoint, files=batch.payload,
verify_ssl=self.verify_ssl)
batch.http_request = http_request
self._worker.send(batch)
self._batch = []
self._payload_size = helpers.TYPICAL_MULTIPART_FOOTER_LENGTH

def _log_process(self, log_req):
"""Process the given log request.
:param log_req: RPRequestLog object
"""
with self._lock:
rq_size = log_req.multipart_size
if self._payload_size + rq_size >= self.max_payload_size:
if len(self._batch) > 0:
self._send_batch()
self._batch.append(log_req)
self._payload_size += rq_size
if len(self._batch) >= self.max_entry_number:
self._send_batch()

def log(self, time, message=None, level=None, attachment=None,
item_id=None):
"""Log message. Can be added to test item in any state.
:param time: Log time
:param message: Log message
:param level: Log level
:param attachment: Attachments(images,files,etc.)
:param item_id: parent item UUID
"""
if item_id is NOT_FOUND:
logger.warning("Attempt to log to non-existent item")
return
rp_file = RPFile(**attachment) if attachment else None
rp_log = RPRequestLog(self.launch_id, time, rp_file, item_id,
level, message)
self._log_process(rp_log)

def start(self):
"""Create a new instance of the Worker class and start it."""
if not self._worker:
# the worker might be already created in case of deserialization
self._worker = APIWorker(self.queue)
self._worker.start()

def stop(self):
"""Send last batches to the worker followed by the stop command."""
if self._worker:
with self._lock:
if self._batch:
self._send_batch()
logger.debug('Waiting for worker {0} to complete'
'processing batches.'.format(self._worker))
self._worker.stop()
warnings.warn(
message="`core.log_manager` is deprecated since 5.2.4 and will be subject "
"for removing in the next major version. Use logs.log_manager` "
"instead",
category=DeprecationWarning,
stacklevel=2
)

def stop_force(self):
"""Send stop immediate command to the worker."""
if self._worker:
self._worker.stop_immediate()
__all__ = [
'LogManager',
'MAX_LOG_BATCH_PAYLOAD_SIZE'
]
20 changes: 10 additions & 10 deletions reportportal_client/core/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ def _command_process(self, cmd):
else:
self._stop()

def _request_process(self, request):
"""Send request to RP and update response attribute of the request."""
logger.debug('[%s] Processing {%s} request', self.name, request)
try:
request.response = request.http_request.make()
except Exception as err:
logger.exception('[%s] Unknown exception has occurred. '
'Skipping it.', err)
self._queue.task_done()

def _monitor(self):
"""Monitor worker queues and process them.
Expand All @@ -111,16 +121,6 @@ def _monitor(self):
logger.debug('[%s] Received {%s} request', self.name, cmd)
self._request_process(cmd)

def _request_process(self, request):
"""Send request to RP and update response attribute of the request."""
logger.debug('[%s] Processing {%s} request', self.name, request)
try:
request.response = request.http_request.make()
except Exception as err:
logger.exception('[%s] Unknown exception has occurred. Terminating'
' the worker.', err)
self._queue.task_done()

def _stop(self):
"""Routine that stops the worker thread(s).
Expand Down
4 changes: 2 additions & 2 deletions reportportal_client/core/worker.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ class APIWorker:

def _command_process(self, cmd: Optional[ControlCommand]) -> None: ...

def _monitor(self) -> None: ...

def _request_process(self, request: Optional[RPRequest]) -> None: ...

def _monitor(self) -> None: ...

def _stop(self) -> None: ...

def _stop_immediately(self) -> None: ...
Expand Down
Loading

0 comments on commit cf52b15

Please sign in to comment.