Skip to content

Commit

Permalink
Lazily import libraries when needed (#1114)
Browse files Browse the repository at this point in the history
* Lazily import libraries when needed

* Poetry update

* Check docs building when failing to include module

* Fix and test docstring meta-driven concatenation

* Release note and Poetry update (main: 27.4.2)
  • Loading branch information
cmin764 authored Oct 13, 2023
1 parent e907182 commit 994658f
Show file tree
Hide file tree
Showing 10 changed files with 208 additions and 154 deletions.
12 changes: 12 additions & 0 deletions docs/source/releasenotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ Latest versions
`Released <https://pypi.org/project/rpaframework/#history>`_
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

27.4.2 - 13 Oct 2023
--------------------

- `Application` classes from **RPA.*.Application** libraries can be further inherited
and extended without the need of adding a mandatory class-level docstring.
- Library **RPA.Robocorp.WorkItems** won't get easily affected anymore by any issue
within the **RPA.Email.ImapSmtp** library.
- Keyword ``Email To Document`` raises appropriately when the input value is of an
unexpected type.
- Takes into use the latest ``robocorp-storage`` **1.0.1**, adding various fixes into
the **RPA.Robocorp.Storage** library.

27.4.1 - 11 Oct 2023
--------------------

Expand Down
15 changes: 13 additions & 2 deletions invocations/docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
import shutil
from pathlib import Path

from invoke import Collection, task
from invoke import Collection, Result, task

from invocations import config, shell
from invocations.util import MAIN_PACKAGE, REPO_ROOT, safely_load_config


EXCLUDES = [
"RPA.Cloud.AWS.textract*",
"RPA.Cloud.Google.keywords*",
Expand All @@ -33,6 +34,8 @@
DOCS_SOURCE_DIR = DOCS_ROOT / "source"
DOCS_BUILD_DIR = DOCS_ROOT / "build" / "html"

FAILURE_TRACES = ["WARNING: autodoc:"]


@task(pre=[config.install, config.install_node], aliases=("libdocs",))
def build_libdocs(ctx):
Expand Down Expand Up @@ -73,6 +76,13 @@ def build_libdocs(ctx):
)


def _check_documentation_build(run_result: Result):
lines = f"{run_result.stdout}\n{run_result.stderr}".splitlines()
for line in lines:
if any(trace in line for trace in FAILURE_TRACES):
raise RuntimeError(line)


@task(pre=[config.install, build_libdocs], default=True, aliases=("build",))
def build_docs(ctx):
"""Builds documentation locally. These can then be browsed directly
Expand Down Expand Up @@ -104,7 +114,8 @@ def build_docs(ctx):
docs_source / "json",
docs_source / "include" / "latest.json",
)
shell.sphinx(ctx, f"-b html -j auto {docs_source} {docs_target}")
run_result = shell.sphinx(ctx, f"-b html -j auto {docs_source} {docs_target}")
_check_documentation_build(run_result)
shell.meta_tool(ctx, "rss")


Expand Down
13 changes: 1 addition & 12 deletions packages/main/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions packages/main/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "rpaframework"
version = "27.4.1"
version = "27.4.2"
description = "A collection of tools and libraries for RPA"
authors = ["RPA Framework <[email protected]>"]
license = "Apache-2.0"
Expand Down Expand Up @@ -76,7 +76,8 @@ click = "^8.1.2"
PyYAML = ">=5.4.1,<7.0.0"
tenacity = "^8.0.1"
htmldocx = "^0.0.6"
python-docx = "^0.8.11"
python-docx = "^0.8.11" # htmldocx
beautifulsoup4 = "^4.7.0" # htmldocx, O365
hubspot-api-client = "^4.0.6"
pyotp = "^2.6.0"
importlib-metadata = "^4.13.0"
Expand Down
16 changes: 7 additions & 9 deletions packages/main/src/RPA/Email/ImapSmtp.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@
)
from typing import Any, BinaryIO, List, Optional, Tuple, Union, Dict

from htmldocx import HtmlToDocx

from RPA.Email.common import (
OAuthMixin,
OAuthProvider,
Expand Down Expand Up @@ -1898,8 +1896,6 @@ def convert_email_to_docx():
lib_work.get_input_work_item()
mail_file = lib_work.get_work_item_file("mail.eml")
lib_mail.email_to_document(mail_file, Path("./output") / "mail.docx")
convert_email_to_docx()
"""

if hasattr(input_source, "read"):
Expand All @@ -1912,17 +1908,19 @@ def convert_email_to_docx():
input_source = self._ensure_path_object(input_source)
self.logger.info("Reading raw e-mail bytes from: %s", input_source)
data = input_source.read_bytes()
assert isinstance(
data, bytes
), "bytes expected for e-mail parsing, got %s" % type(data)
if not isinstance(data, bytes):
raise ValueError(f"bytes expected for e-mail parsing, got {type(data)}")

self.logger.info("Getting the html/text from the raw e-mail")
message = message_from_bytes(data)
body, _ = self.get_decoded_email_body(message, html_first=True)

h2d_parser = HtmlToDocx()
self.logger.debug("Converting html/text content:\n%s", body)
docx = h2d_parser.parse_html_string(body)
# pylint: disable=import-outside-toplevel
from htmldocx import HtmlToDocx

h2d_parser = HtmlToDocx()
docx = h2d_parser.parse_html_string(body.strip())
output_path = self._ensure_path_object(output_path)
self.logger.info("Writing converted document into: %s", output_path)
output_path.parent.mkdir(parents=True, exist_ok=True)
Expand Down
4 changes: 3 additions & 1 deletion packages/main/src/RPA/Robocorp/WorkItems.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from RPA.core.helpers import import_by_name, required_env
from RPA.core.logger import deprecation
from RPA.core.notebook import notebook_print
from RPA.Email.ImapSmtp import ImapSmtp
from RPA.FileSystem import FileSystem
from RPA.Robocorp.utils import (
JSONType,
Expand Down Expand Up @@ -1066,6 +1065,9 @@ def _parse_work_item_from_email(self):
# pylint: disable=no-member
message = email.message_from_string(content)
message_dict = dict(message.items())
# pylint: disable=import-outside-toplevel
from RPA.Email.ImapSmtp import ImapSmtp

body, has_attachments = ImapSmtp().get_decoded_email_body(message)
message_dict["Body"] = self._interpret_content(body)
message_dict["Has-Attachments"] = has_attachments
Expand Down
3 changes: 2 additions & 1 deletion packages/main/src/RPA/SAP.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@
import platform
import time
from typing import Optional
from comtypes import COMError


if platform.system() == "Windows":
# import win32com.client to fix order of imports in the SapGuiLibrary
# pylint: disable=unused-import
import win32com.client # noqa: F401
from SapGuiLibrary import SapGuiLibrary
from comtypes import COMError
else:
logging.getLogger(__name__).warning(
"RPA.SapGuiLibrary library works only on Windows platform"
)
COMError = Exception

class SapGuiLibrary:
"""Keywords are only available in Windows."""
Expand Down
2 changes: 1 addition & 1 deletion packages/main/src/RPA/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def __new__(cls, *args, **kwargs) -> type:
return final_class

super_class = bases[0]
final_class.__doc__ += super_class.__doc__
final_class.__doc__ = (final_class.__doc__ or "") + (super_class.__doc__ or "")
if final_class.APP_DISPATCH is None:
raise ValueError(
"An `APP_DISPATCH` has to be defined in this"
Expand Down
38 changes: 38 additions & 0 deletions packages/main/tests/python/test_applications.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import pytest


class TestOutlook:
"""Gathers `RPA.Outlook.Application` tests."""

@pytest.fixture
def Application(self):
from RPA.Outlook.Application import Application

return Application

@pytest.fixture
def app(self, Application):
return Application()

@pytest.fixture
def custom_app(self, Application):
class CustomApplication(Application):
"""Some test docstring."""

return CustomApplication()

@pytest.fixture
def custom_app_no_docs(self, Application):
class CustomApplication(Application):
pass

return CustomApplication()

def test_import(self, app):
assert app.__doc__.startswith("`Outlook.Application`")

def test_custom_import(self, custom_app):
assert custom_app.__doc__.startswith("Some test docstring.")

def test_custom_import_no_docs(self, custom_app_no_docs):
assert custom_app_no_docs.__doc__.startswith("`Outlook.Application`")
Loading

0 comments on commit 994658f

Please sign in to comment.