Skip to content

Commit

Permalink
Docstring (#7)
Browse files Browse the repository at this point in the history
* pymilo_obj.py docstrings added.

* complete util.py's docstrings

* linear_model_chain.py's docstrings added.

* deserialize_exception.py's docstrings added.

* pymilo_exception.py docstring added, serialize_exception.py docstring added, deserialize_exception.py docstring enhanced.

* transporter.py's docstring added.

* lossfunction_transporter.py's docstring added.

* labelbinarizer_transporter.py's docstring added.

* general_data_structure_transporter.py's docstring added.

* baseloss_transporter.py's docstring added.

* baseloss_transporter.py's docstring enhanced.

* remove the third person 's

* pydocstyle test added to github workflow

* docstrings of super class's functions added to associated child class's functions.

* fix pydocstyle errors for the pymilo_func.py and pymilo_obj.py

* add pydocstyle config file

* update test.yml to run pydocstyle

* fix pydocstyle errors for all python files within chains, exceptions and transporters directories

* pydocstyle added to dev-requirments.txt

* .pydocstyle file updated.

* modify linear_model_chain's docstrings

* modify deserialize_exception docstrings

* modify pymilo_exceptions docstrings

* Modify serialize_exceptions docstrings

* Modify baseloss_transporter docstrings

* Modify docstring

* Modify docstring

* Modify docstrings

* Modify docstring

* Modify transporter docstring

* modify pymilo_func docstrings

* Modify pymilo_obj docstrings

* Minor edits on docstrings

* apply consistency fix requested by sadra

---------

Co-authored-by: alirezazolanvari <[email protected]>
  • Loading branch information
AHReccese and alirezazolanvari authored Jun 6, 2023
1 parent 12c9925 commit d624cd2
Show file tree
Hide file tree
Showing 15 changed files with 501 additions and 23 deletions.
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,5 @@ jobs:
run: |
python -m vulture pymilo/ otherfiles/ setup.py --min-confidence 65 --exclude=__init__.py --sort-by-size
python -m bandit -r pymilo -s B311
python -m pydocstyle
if: matrix.python-version == 3.8
4 changes: 4 additions & 0 deletions .pydocstyle
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[pydocstyle]
match_dir = ^(?!(tests|build)).*
match = .*\.py

3 changes: 2 additions & 1 deletion dev-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
setuptools>=40.8.0
vulture>=1.0
bandit>=1.5.1
bandit>=1.5.1
pydocstyle>=3.0.0
56 changes: 55 additions & 1 deletion pymilo/chains/linear_model_chain.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
"""PyMilo chain for linear models."""
from ..transporters.transporter import Command

from ..transporters.general_data_structure_transporter import GeneralDataStructureTransporter
Expand All @@ -21,17 +23,41 @@


def is_linear_model(model):
"""
Check if the input model is a sklearn's linear model.
:param model: given model
:type model: any object
:return: check result as bool
"""
return type(model) in SKLEARN_LINEAR_MODEL_TABLE.values()


def is_deserialized_linear_model(content):
"""
Check if the given content is a previously serialized model by Pymilo's Export or not.
:param content: given object to be authorized as a valid pymilo exported serialized model
:type content: any object
:return: check result as bool
"""
if not is_iterable(content):
return False
return "inner-model-type" in content and "inner-model-data" in content


def transport_linear_model(request, command, is_inner_model=False):

"""
Return the transported (Serialized or Deserialized) model.
:param request: given model to be transported
:type request: any object
:param command: command to specify whether the request should be serialized or deserialized
:type command: transporter.Command
:param is_inner_model: determines whether the request is an inner linear model, as a single field of a wrapper linear model
:type is_inner_model: boolean
:return: the transported request as a json string or sklearn linear model
"""
if not is_inner_model:
validate_input(request, command, is_inner_model)

Expand Down Expand Up @@ -61,6 +87,13 @@ def transport_linear_model(request, command, is_inner_model=False):


def serialize_linear_model(linear_model_object):
"""
Return the serialized json string of the given linear model.
:param linear_model_object: given model to be get serialized
:type linear_model_object: any sklearn linear model
:return: the serialized json string of the given linear model
"""
# first serializing the inner linear models...
for key in linear_model_object.__dict__.keys():
if is_linear_model(linear_model_object.__dict__[key]):
Expand All @@ -77,6 +110,15 @@ def serialize_linear_model(linear_model_object):


def deserialize_linear_model(linear_model, is_inner_model):
"""
Return the associated sklearn linear model of the given linear_model.
:param linear_model: given json string of a linear model to get deserialized to associated sklearn linear model
:type linear_model: obj
:param is_inner_model: determines whether the request is an inner linear model, as a single field of a wrapper linear model
:type is_inner_model: boolean
:return: associated sklearn linear model
"""
raw_model = None
data = None
if is_inner_model:
Expand All @@ -103,6 +145,18 @@ def deserialize_linear_model(linear_model, is_inner_model):


def validate_input(model, command, is_inner_model):
"""
Check if the provided inputs are valid in relation to each other.
:param model: given object to gets transported, whether a sklearn linear model to get serialized
or a json string of a linear model to get deserialized to associated sklearn linear model
:type model: obj
:param command: command to specify whether the request should be serialized or deserialized
:type command: transporter.Command
:param is_inner_model: determines whether the request is an inner linear model, as a single field of a wrapper linear model
:type is_inner_model: boolean
:return: None
"""
if command == Command.SERIALIZE:
if get_sklearn_type(model) in SKLEARN_LINEAR_MODEL_TABLE.keys():
return
Expand Down
37 changes: 37 additions & 0 deletions pymilo/exceptions/deserialize_exception.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,42 @@
# -*- coding: utf-8 -*-
"""PyMilo Deserialization Exception."""
from enum import Enum
from .pymilo_exception import PymiloException


class DeSerilaizatoinErrorTypes(Enum):
"""An enum class to determine the type of deserialization errors."""

CORRUPTED_JSON_FILE = 1
INVALID_MODEL = 2
VALID_MODEL_INVALID_INTERNAL_STRUCTURE = 3


class PymiloDeserializationException(PymiloException):
"""
Handle exceptions associated with Deserializations.
There are 3 different types of deserialization exceptions:
1-CORRUPTED_JSON_FILE: This error type claims that the given json string file which is supposed to be an
output of Pymilo Export, is corrupted and can not be parsed as a valid json.
2-INVALID_MODEL: This error type claims that the given json string file(or object) is not a deserialized export of
a valid sklearn linear model.
3-VALID_MODEL_INVALID_INTERNAL_STRUCTURE: This error occurs when attempting to load a JSON file or object that
does not conform to the expected format of a serialized scikit-learn linear model.
The file may have been modified after being exported from Pymilo Export, causing it to become invalid.
"""

def __init__(self, meta_data):
"""
Initialize the PymiloDeserializationException instance.
:param meta_data: Details pertain to the populated error.
:type meta_data: dict [str:str]
:return: an intance of the PymiloDeserializationException class
"""
# Call the base class constructor with the parameters it needs
message = "Pymilo Deserialization failed since {reason}"
error_type = meta_data['error_type']
Expand All @@ -25,12 +52,22 @@ def __init__(self, meta_data):
super().__init__(message, meta_data)

def to_pymilo_log(self):
"""
Generate a comprehensive report of the populated error.
:return: a dictionary of error details.
"""
pymilo_report = super().to_pymilo_log()
if self.meta_data['error_type'] == DeSerilaizatoinErrorTypes.CORRUPTED_JSON_FILE:
pymilo_report['object']['json_file'] = self.meta_data['json_file']
return pymilo_report

def to_pymilo_issue(self):
"""
Generate an issue form from the populated error.
:return: issue form of the associated error as a string
"""
pymilo_report = self.to_pymilo_log()
help_request = "\n\nIn order to help us enhance Pymilo's functionality, please open an issue associated with this error and put the message below inside.\n"
discription = "#### Description\n Pymilo Import failed."
Expand Down
29 changes: 29 additions & 0 deletions pymilo/exceptions/pymilo_exception.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
"""PyMilo Abstract Exception Class."""

import pymilo
import sklearn
import platform
Expand All @@ -6,7 +9,18 @@


class PymiloException(Exception, ABC):
"""An abstract class for handling pymilo associated exceptions."""

def __init__(self, message, meta_data):
"""
Initialize the PymiloException instance.
:param message: Error message associated with the populated error.
:type message: str
:param meta_data: Details pertain to the populated error.
:type meta_data: dict [str:str]
:return: an intance of the PymiloDeserializationException class
"""
# Call the base class constructor with the parameters it needs
super().__init__(message)
# gathered meta_data
Expand All @@ -15,6 +29,11 @@ def __init__(self, message, meta_data):

# collect All pymilo related data.
def to_pymilo_log(self):
"""
Generate a comprehensive report of the populated error.
:return: error's details as dictionary
"""
pymilo_report = {
'os': {
'name': platform.system(),
Expand Down Expand Up @@ -42,7 +61,17 @@ def to_pymilo_log(self):

@abstractmethod
def to_pymilo_issue(self):
"""
Generate an issue form from the populated error.
:return: issue form of the associated error as string
"""
pass

def __str__(self):
"""
Override the base __str__ function.
:return: issue form of the associated error as string
"""
return self.to_pymilo_issue()
33 changes: 33 additions & 0 deletions pymilo/exceptions/serialize_exception.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,37 @@
# -*- coding: utf-8 -*-
"""PyMilo Serialization Exception."""

from enum import Enum
from .pymilo_exception import PymiloException


class SerilaizatoinErrorTypes(Enum):
"""An enum class used to determine the type of serialization errors."""

INVALID_MODEL = 1
VALID_MODEL_INVALID_INTERNAL_STRUCTURE = 2


class PymiloSerializationException(PymiloException):
"""
Handle exceptions associated with Serializations.
There are 2 different types of serialization exceptions:
1-INVALID_MODEL: This error type claims that the given model is not a valid sklearn's linear model.
2-VALID_MODEL_INVALID_INTERNAL_STRUCTURE: This error occurs when attempting to serialize a model that
is one of the sklearn's linear models but it's internal structure has changed in a way that can't be serialized.
"""

def __init__(self, meta_data):
"""
Initialize the PymiloSerializationException instance.
:param meta_data: Details pertain to the populated error.
:type meta_data: dict [str:str]
:return: an intance of the PymiloSerializationException class
"""
# Call the base class constructor with the parameters it needs
message = "Pymilo Serialization failed since "
error_type = meta_data['error_type']
Expand All @@ -22,11 +45,21 @@ def __init__(self, meta_data):
super().__init__(message, meta_data)

def to_pymilo_log(self):
"""
Generate a comprehensive report of the populated error.
:return: error's details as dictionary
"""
pymilo_report = super().to_pymilo_log()
# TODO add any serializable field to `object` field of pymilo_report
return pymilo_report

def to_pymilo_issue(self):
"""
Generate an issue form from the populated error.
:return: issue form of the associated error as string
"""
pymilo_report = self.to_pymilo_log()
help_request = "\n\nIn order to help us enhance Pymilo's functionality, please open an issue associated with this error and put the message below inside.\n"
discription = "#### Description\n Pymilo Export failed."
Expand Down
8 changes: 4 additions & 4 deletions pymilo/pymilo_func.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def get_sklearn_version():

def get_sklearn_data(model):
"""
Return sklearn data by serializing given model
Return sklearn data by serializing given model.
:param model: given model
:type model: any sklearn's model class
Expand All @@ -28,7 +28,7 @@ def get_sklearn_data(model):

def to_sklearn_model(import_obj):
"""
Return sklearn model by deserializing imported object
Deserialize the imported object as a sklearn model.
:param import_obj: given object
:type import_obj: pymilo.Import
Expand All @@ -41,15 +41,15 @@ def compare_model_outputs(exported_output,
imported_output,
epsilon_error=10**(-8)):
"""
Compare given models outputs.
Check if the given models outputs are the same.
:param exported_output: exported model output
:type exported_output: dict
:param imported_output: imported model output
:type imported_output: dict
:param epsilon_error: error threshold for numeric comparisons
:type epsilon_error: float
:return: True if two outputs are the same
:return: check result as bool
"""
if len(exported_output.keys()) != len(imported_output.keys()):
return False # TODO: throw exception
Expand Down
29 changes: 27 additions & 2 deletions pymilo/pymilo_obj.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,21 @@

class Export:
"""
TODO: Complete docstring.
The Pymilo Export class facilitates exporting of models to json files.
>>> exported_model = Export(model) # the model could be any sklearn linear model.
>>> exported_model_serialized_path = os.path.join(os.getcwd(), "MODEL_NAME.json")
>>> exported_model.save(exported_model_serialized_path)
"""

def __init__(self, model):
"""
Initialize the Pymilo Export instance.
:param model: given model(any sklearn linear model)
:type model: any class of the sklearn's linear models
:return: an instance of the Pymilo Export class
"""
self.data = get_sklearn_data(model)
self.version = get_sklearn_version()
self.type = get_sklearn_type(model)
Expand All @@ -34,6 +45,7 @@ def save(self, file_adr):
def to_json(self):
"""
Return a json-like representation of model.
:return: model's representation as str
"""
try:
Expand Down Expand Up @@ -63,10 +75,23 @@ def to_json(self):

class Import:
"""
TODO: Complete docstring.
The Pymilo Import class facilitates importing of serialized models from either a designated file path or a JSON string dump.
>>> imported_model = Import(exported_model_serialized_path)
>>> imported_sklearn_model = imported_model.to_model()
>>> imported_sklearn_model.predict(x_test)
"""

def __init__(self, file_adr, json_dump=None):
"""
Initialize the Pymilo Import instance.
:param file_adr: the file path where the serialized model's JSON file is located.
:type file_adr: string
:param json_dump: the json dump of the associated model, it can be None(reading from the file_adr)
:type json_dump: str or None
:return: an instance of the Pymilo Import class
"""
serialized_model_obj = None
try:
if json_dump and isinstance(json_dump, str):
Expand Down
Loading

0 comments on commit d624cd2

Please sign in to comment.