Skip to content

archan ¤

Archan package.

The purpose of this package is to make possible the analysis of a problem using a DSM (Design Structure Matrix) on which certain criteria will be verified.

Modules:

  • analysis

    Analysis module.

  • cli

    Module that contains the command line application.

  • config

    Configuration module.

  • debug

    Debugging utilities.

  • dsm

    DSM module.

  • enums

    Enumerations module.

  • errors

    Errors module.

  • logging

    Logging module.

  • plugins

    Plugins submodule.

  • printing

    Printing module.

Classes:

Argument ¤

Argument(
    name: str,
    cls: type,
    description: str,
    default: Any | None = None,
)

Bases: PrintableArgumentMixin

Placeholder for name, class, description and default value.

Parameters:

  • name (str) –

    Name of the argument.

  • cls (type) –

    Type of the argument.

  • description (str) –

    Description of the argument.

  • default (Any | None, default: None ) –

    Default value for the argument.

Methods:

  • print

    Print self with optional indent.

Source code in src/archan/plugins/__init__.py
21
22
23
24
25
26
27
28
29
30
31
32
33
def __init__(self, name: str, cls: type, description: str, default: Any | None = None):
    """Initialization method.

    Parameters:
        name: Name of the argument.
        cls: Type of the argument.
        description: Description of the argument.
        default: Default value for the argument.
    """
    self.name = name
    self.cls = cls
    self.description = description
    self.default = default

print ¤

print(indent: int = 0) -> None

Print self with optional indent.

Parameters:

  • indent (int, default: 0 ) –

    Indentation.

Source code in src/archan/printing.py
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
def print(self, indent: int = 0) -> None:  # noqa: A003
    """Print self with optional indent.

    Parameters:
        indent: Indentation.
    """
    text = "{indent}{magenta}{name}{none} ({dim}{cls}{none}, default {dim}{default}{none})".format(
        indent=" " * indent,
        dim=Style.DIM,
        magenta=Fore.MAGENTA,
        none=Style.RESET_ALL,
        name=self.name,  # type: ignore[attr-defined]
        cls=self.cls,  # type: ignore[attr-defined]
        default=self.default,  # type: ignore[attr-defined]
    )

    if self.description:  # type: ignore[attr-defined]
        text += ":\n" + pretty_description(self.description, indent=indent + 2)  # type: ignore[attr-defined]

    print(text)

Checker ¤

Checker(
    name: str | None = None,
    description: str | None = None,
    hint: str | None = None,
    allow_failure: bool = False,
    passes: Any | None = None,
    arguments: dict | None = None,
)

Bases: PrintableNameMixin, PrintablePluginMixin

Checker class.

An instance of Checker implements a check method that analyzes an instance of DSM/DMM/MDM and return a true or false value, with optional message.

Parameters:

  • name (str | None, default: None ) –

    The checker name.

  • description (str | None, default: None ) –

    The checker description.

  • hint (str | None, default: None ) –

    Hint provided for failures.

  • allow_failure (bool, default: False ) –

    Still pass if failed or not.

  • passes (Any | None, default: None ) –

    Boolean.

  • arguments (dict | None, default: None ) –

    Arguments passed to the check method when run.

Methods:

  • check

    Check the data and return a result.

  • print

    Print self.

  • print_name

    Print name with optional indent and end.

  • run

    Run the check method and format the result for analysis.

Source code in src/archan/plugins/__init__.py
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def __init__(
    self,
    name: str | None = None,
    description: str | None = None,
    hint: str | None = None,
    allow_failure: bool = False,  # noqa: FBT001, FBT002
    passes: Any | None = None,
    arguments: dict | None = None,
):
    """Initialization method.

    Parameters:
        name: The checker name.
        description: The checker description.
        hint: Hint provided for failures.
        allow_failure: Still pass if failed or not.
        passes: Boolean.
        arguments: Arguments passed to the check method when run.
    """
    if name:
        self.name = name
    if description:
        self.description = description
    if hint:
        self.hint = hint

    self.allow_failure = allow_failure
    self.passes = passes
    self.arguments = arguments or {}
    self.result = None

check ¤

Check the data and return a result.

Parameters:

Returns:

  • result ( Any ) –

    Checker constant or object with a __bool__ method.

  • message ( str ) –

    Optional messages.

Source code in src/archan/plugins/__init__.py
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
def check(
    self,
    dsm: DesignStructureMatrix | MultipleDomainMatrix | DomainMappingMatrix,
    **kwargs: Any,
) -> tuple[Any, str]:
    """Check the data and return a result.

    Parameters:
        dsm: DSM/DMM/MDM instance to check.
        **kwargs: Additional arguments.

    Returns:
        result: Checker constant or object with a ``__bool__`` method.
        message: Optional messages.
    """
    raise NotImplementedError

print ¤

print() -> None

Print self.

Source code in src/archan/printing.py
115
116
117
118
119
120
121
122
123
124
125
126
def print(self) -> None:  # noqa: A003
    """Print self."""
    print(
        f"{Style.DIM}Identifier:{Style.RESET_ALL} {Fore.CYAN}{self.identifier}{Style.RESET_ALL}\n"
        f"{Style.DIM}Name:{Style.RESET_ALL} {self.name}\n"
        f"{Style.DIM}Description:{Style.RESET_ALL}\n{pretty_description(self.description, indent=2)}",
    )

    if hasattr(self, "argument_list") and self.argument_list:
        print(f"{Style.DIM}Arguments:{Style.RESET_ALL}")
        for argument in self.argument_list:
            argument.print(indent=2)

print_name ¤

print_name(indent: int = 0, end: str = '\n') -> None

Print name with optional indent and end.

Parameters:

  • indent (int, default: 0 ) –

    Indentation.

  • end (str, default: '\n' ) –

    End of line.

Source code in src/archan/printing.py
73
74
75
76
77
78
79
80
def print_name(self, indent: int = 0, end: str = "\n") -> None:
    """Print name with optional indent and end.

    Parameters:
        indent: Indentation.
        end: End of line.
    """
    print(Style.BRIGHT + " " * indent + self.name, end=end)  # type: ignore[attr-defined]

run ¤

Run the check method and format the result for analysis.

Parameters:

Source code in src/archan/plugins/__init__.py
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
def run(self, data: DesignStructureMatrix | MultipleDomainMatrix | DomainMappingMatrix) -> None:
    """Run the check method and format the result for analysis.

    Parameters:
        data: DSM/DMM/MDM instance to check.
    """
    result_type = namedtuple("Result", "code messages")  # type: ignore[name-match]  # noqa: PYI024

    if self.passes is True:
        result = result_type(Checker.Code.PASSED, "")
    elif self.passes is False:
        result = (
            result_type(Checker.Code.IGNORED, "") if self.allow_failure else result_type(Checker.Code.FAILED, "")
        )
    else:
        try:
            result = self.check(data, **self.arguments)  # type: ignore[assignment]
        except NotImplementedError:
            result = result_type(Checker.Code.NOT_IMPLEMENTED, "")
        else:
            messages = ""
            if isinstance(result, tuple):
                result, messages = result

            if result not in Checker.Code:
                result = Checker.Code.PASSED if bool(result) else Checker.Code.FAILED  # type: ignore[assignment]

            if result == Checker.Code.FAILED and self.allow_failure:
                result = Checker.Code.IGNORED  # type: ignore[assignment]

            result = result_type(result, messages)
    self.result = result  # type: ignore[assignment]

DesignStructureMatrix ¤

DesignStructureMatrix(
    data: list[list[int | float]],
    entities: list | None = None,
    categories: list | None = None,
)

Bases: BaseMatrix

Design Structure Matrix class.

Parameters:

  • data (list[list[int | float]]) –

    2-dim array.

  • entities (list | None, default: None ) –

    List of entities.

  • categories (list | None, default: None ) –

    List of the categories (one per entity).

Methods:

Attributes:

  • columns (int) –

    Return number of columns in data.

  • rows (int) –

    Return number of rows in data.

  • size (tuple[int, int]) –

    Return number of rows and columns in data.

Source code in src/archan/dsm.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
def __init__(self, data: list[list[int | float]], entities: list | None = None, categories: list | None = None):
    """Initialization method.

    Parameters:
        data: 2-dim array.
        entities: List of entities.
        categories: List of the categories (one per entity).
    """
    self.data = data
    if entities is None:
        entities = self.default_entities()
    self.entities = entities
    if categories is None:
        categories = []
    self.categories = categories

    self.validate()

columns property ¤

columns: int

Return number of columns in data.

Returns:

  • int

    The number of columns.

rows property ¤

rows: int

Return number of rows in data.

Returns:

  • int

    The number of rows.

size property ¤

size: tuple[int, int]

Return number of rows and columns in data.

Returns:

default_entities ¤

default_entities() -> list[str]

Default entities used when there are none.

Returns:

  • list[str]

    The default entities.

Source code in src/archan/dsm.py
141
142
143
144
145
146
147
def default_entities(self) -> list[str]:
    """Default entities used when there are none.

    Returns:
        The default entities.
    """
    return [str(_) for _ in range(self.rows)]

transitive_closure ¤

transitive_closure() -> list[list[int]]

Compute the transitive closure of the matrix.

Returns:

  • list[list[int]]

    The transitive closure of the matrix.

Source code in src/archan/dsm.py
167
168
169
170
171
172
173
174
175
176
177
178
179
def transitive_closure(self) -> list[list[int]]:
    """Compute the transitive closure of the matrix.

    Returns:
        The transitive closure of the matrix.
    """
    data = [[1 if j else 0 for j in i] for i in self.data]
    for k in range(self.rows):
        for i in range(self.rows):
            for j in range(self.rows):
                if data[i][k] and data[k][j]:
                    data[i][j] = 1
    return data

validate ¤

validate() -> None

Base validation + entities = rows.

Raises:

Source code in src/archan/dsm.py
156
157
158
159
160
161
162
163
164
165
def validate(self) -> None:
    """Base validation + entities = rows.

    Raises:
        DesignStructureMatrixError: When number of entities is different than number of rows.
    """
    super().validate()
    nb_entities = len(self.entities)
    if nb_entities != self.rows:
        raise self.error(f"Number of entities: {nb_entities} != number of rows: {self.rows}")

DomainMappingMatrix ¤

DomainMappingMatrix(
    data: list[list[int | float]],
    entities: list | None = None,
    categories: list | None = None,
)

Bases: BaseMatrix

Domain Mapping Matrix class.

Parameters:

  • data (list[list[int | float]]) –

    2-dim array.

  • entities (list | None, default: None ) –

    List of entities.

  • categories (list | None, default: None ) –

    List of the categories (one per entity).

Methods:

Attributes:

  • columns (int) –

    Return number of columns in data.

  • rows (int) –

    Return number of rows in data.

  • size (tuple[int, int]) –

    Return number of rows and columns in data.

Source code in src/archan/dsm.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
def __init__(self, data: list[list[int | float]], entities: list | None = None, categories: list | None = None):
    """Initialization method.

    Parameters:
        data: 2-dim array.
        entities: List of entities.
        categories: List of the categories (one per entity).
    """
    self.data = data
    if entities is None:
        entities = self.default_entities()
    self.entities = entities
    if categories is None:
        categories = []
    self.categories = categories

    self.validate()

columns property ¤

columns: int

Return number of columns in data.

Returns:

  • int

    The number of columns.

rows property ¤

rows: int

Return number of rows in data.

Returns:

  • int

    The number of rows.

size property ¤

size: tuple[int, int]

Return number of rows and columns in data.

Returns:

default_entities ¤

default_entities() -> list[str]

Return range from 0 to rows + columns.

Returns:

  • list[str]

    Range from 0 to rows + columns.

Source code in src/archan/dsm.py
201
202
203
204
205
206
207
def default_entities(self) -> list[str]:
    """Return range from 0 to rows + columns.

    Returns:
        Range from 0 to rows + columns.
    """
    return [str(_) for _ in range(self.rows + self.columns)]

validate ¤

validate() -> None

Base validation + entities = rows + columns.

Raises:

Source code in src/archan/dsm.py
187
188
189
190
191
192
193
194
195
196
197
198
199
def validate(self) -> None:
    """Base validation + entities = rows + columns.

    Raises:
        DomainMappingMatrixError: When number of entities is different than rows plus columns.
    """
    super().validate()
    nb_entities = len(self.entities)
    if nb_entities != self.rows + self.columns:
        raise self.error(
            f"Number of entities: {nb_entities} != number of rows + "
            f"number of columns: {self.rows}+{self.columns}={self.rows + self.columns}",
        )

Logger ¤

Static class to store loggers.

Methods:

get_logger staticmethod ¤

get_logger(
    name: str,
    level: int | None = None,
    fmt: str = ":%(lineno)d: %(message)s",
) -> Logger

Return a logger.

Parameters:

  • name (str) –

    Name to pass to the logging module.

  • level (int | None, default: None ) –

    Level of logging.

  • fmt (str, default: ':%(lineno)d: %(message)s' ) –

    Format string.

Returns:

  • Logger

    Logger from logging.getLogger.

Source code in src/archan/logging.py
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@staticmethod
def get_logger(name: str, level: int | None = None, fmt: str = ":%(lineno)d: %(message)s") -> logging.Logger:
    """Return a logger.

    Parameters:
        name: Name to pass to the logging module.
        level: Level of logging.
        fmt: Format string.

    Returns:
        Logger from ``logging.getLogger``.
    """
    if name not in Logger.loggers:
        if Logger.level is None and level is None:
            Logger.level = logging.ERROR
            level = logging.ERROR
        elif Logger.level is None:
            Logger.level = level
        elif level is None:
            level = Logger.level
        logger = logging.getLogger(name)
        logger_handler = logging.StreamHandler()
        logger_handler.setFormatter(LoggingFormatter(fmt=name + fmt))
        logger.addHandler(logger_handler)
        logger.setLevel(level)  # type: ignore[arg-type]
        Logger.loggers[name] = logger
    return Logger.loggers[name]

set_level staticmethod ¤

set_level(level: int) -> None

Set level of logging for all loggers.

Parameters:

  • level (int) –

    Level of logging.

Source code in src/archan/logging.py
17
18
19
20
21
22
23
24
25
26
@staticmethod
def set_level(level: int) -> None:
    """Set level of logging for all loggers.

    Parameters:
        level: Level of logging.
    """
    Logger.level = level
    for logger in Logger.loggers.values():
        logger.setLevel(level)

MultipleDomainMatrix ¤

MultipleDomainMatrix(
    data: list[list[int | float]],
    entities: list | None = None,
    categories: list | None = None,
)

Bases: BaseMatrix

Multiple Domain Matrix class.

Parameters:

  • data (list[list[int | float]]) –

    2-dim array.

  • entities (list | None, default: None ) –

    List of entities.

  • categories (list | None, default: None ) –

    List of the categories (one per entity).

Methods:

  • default_entities

    Default entities used when there are none.

  • validate

    Base validation + each cell is instance of DSM or MDM.

Attributes:

  • columns (int) –

    Return number of columns in data.

  • rows (int) –

    Return number of rows in data.

  • size (tuple[int, int]) –

    Return number of rows and columns in data.

Source code in src/archan/dsm.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
def __init__(self, data: list[list[int | float]], entities: list | None = None, categories: list | None = None):
    """Initialization method.

    Parameters:
        data: 2-dim array.
        entities: List of entities.
        categories: List of the categories (one per entity).
    """
    self.data = data
    if entities is None:
        entities = self.default_entities()
    self.entities = entities
    if categories is None:
        categories = []
    self.categories = categories

    self.validate()

columns property ¤

columns: int

Return number of columns in data.

Returns:

  • int

    The number of columns.

rows property ¤

rows: int

Return number of rows in data.

Returns:

  • int

    The number of rows.

size property ¤

size: tuple[int, int]

Return number of rows and columns in data.

Returns:

default_entities ¤

default_entities() -> list[str]

Default entities used when there are none.

Returns:

  • list[str]

    The default entities.

Source code in src/archan/dsm.py
141
142
143
144
145
146
147
def default_entities(self) -> list[str]:
    """Default entities used when there are none.

    Returns:
        The default entities.
    """
    return [str(_) for _ in range(self.rows)]

validate ¤

validate() -> None

Base validation + each cell is instance of DSM or MDM.

Raises:

Source code in src/archan/dsm.py
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
def validate(self) -> None:
    """Base validation + each cell is instance of DSM or MDM.

    Raises:
        MultipleDomainMatrixError: When diagonal cells are not DSM nor MDM, or when other cells are not DMM nor MDM.
    """
    super().validate()
    message_dsm = "Matrix at [%s:%s] is not an instance of DesignStructureMatrix or MultipleDomainMatrix."
    message_ddm = "Matrix at [%s:%s] is not an instance of DomainMappingMatrix or MultipleDomainMatrix."
    messages = []
    for line, row in enumerate(self.data):
        for column, cell in enumerate(row):
            if line == column:
                if not isinstance(cell, (DesignStructureMatrix, MultipleDomainMatrix)):
                    messages.append(message_dsm % (line, column))
            elif not isinstance(cell, (DomainMappingMatrix, MultipleDomainMatrix)):
                messages.append(message_ddm % (line, column))
    if messages:
        raise self.error("\n".join(messages))

Provider ¤

Provider(
    name: str | None = None,
    description: str | None = None,
    arguments: dict | None = None,
)

Bases: PrintableNameMixin, PrintablePluginMixin

Provider class.

An instance of provider implements a get_data method that returns an instance of DSM/DMM/MDM to be checked by an instance of Checker.

Parameters:

  • name (str | None, default: None ) –

    The provider name.

  • description (str | None, default: None ) –

    The provider description.

  • arguments (dict | None, default: None ) –

    Arguments that will be used for get_data method.

Methods:

  • get_data

    Abstract method. Return instance of DSM/DMM/MDM.

  • print

    Print self.

  • print_name

    Print name with optional indent and end.

  • run

    Run the get_data method with run arguments, store the result.

Source code in src/archan/plugins/__init__.py
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
def __init__(
    self,
    name: str | None = None,
    description: str | None = None,
    arguments: dict | None = None,
) -> None:
    """Initialization method.

    Parameters:
        name: The provider name.
        description: The provider description.
        arguments: Arguments that will be used for `get_data` method.
    """
    if name:
        self.name = name
    if description:
        self.description = description

    self.arguments = arguments or {}
    self.data = None

get_data ¤

get_data(**kwargs: Any) -> Any

Abstract method. Return instance of DSM/DMM/MDM.

Parameters:

  • **kwargs (Any, default: {} ) –

    Keyword arguments.

Raises:

Source code in src/archan/plugins/__init__.py
170
171
172
173
174
175
176
177
178
179
def get_data(self, **kwargs: Any) -> Any:
    """Abstract method. Return instance of DSM/DMM/MDM.

    Parameters:
        **kwargs: Keyword arguments.

    Raises:
        NotImplementedError: This method must be implemented in subclasses.
    """
    raise NotImplementedError

print ¤

print() -> None

Print self.

Source code in src/archan/printing.py
115
116
117
118
119
120
121
122
123
124
125
126
def print(self) -> None:  # noqa: A003
    """Print self."""
    print(
        f"{Style.DIM}Identifier:{Style.RESET_ALL} {Fore.CYAN}{self.identifier}{Style.RESET_ALL}\n"
        f"{Style.DIM}Name:{Style.RESET_ALL} {self.name}\n"
        f"{Style.DIM}Description:{Style.RESET_ALL}\n{pretty_description(self.description, indent=2)}",
    )

    if hasattr(self, "argument_list") and self.argument_list:
        print(f"{Style.DIM}Arguments:{Style.RESET_ALL}")
        for argument in self.argument_list:
            argument.print(indent=2)

print_name ¤

print_name(indent: int = 0, end: str = '\n') -> None

Print name with optional indent and end.

Parameters:

  • indent (int, default: 0 ) –

    Indentation.

  • end (str, default: '\n' ) –

    End of line.

Source code in src/archan/printing.py
73
74
75
76
77
78
79
80
def print_name(self, indent: int = 0, end: str = "\n") -> None:
    """Print name with optional indent and end.

    Parameters:
        indent: Indentation.
        end: End of line.
    """
    print(Style.BRIGHT + " " * indent + self.name, end=end)  # type: ignore[attr-defined]

run ¤

run() -> None

Run the get_data method with run arguments, store the result.

Source code in src/archan/plugins/__init__.py
181
182
183
def run(self) -> None:
    """Run the get_data method with run arguments, store the result."""
    self.data = self.get_data(**self.arguments)