StrictDoc Documentation
strictdoc/server/reload_config.py
Source file coverage
Path:
strictdoc/server/reload_config.py
Lines:
128
Non-empty lines:
112
Non-empty lines covered with requirements:
112 / 112 (100.0%)
Functions:
3
Functions covered by requirements:
3 / 3 (100.0%)
1
"""
2
@relation(SDOC-SRS-126, scope=file)
3
"""
4
 
5
import os
6
from dataclasses import dataclass
7
from typing import List, Optional
8
 
9
from strictdoc.commands.server_config import ServerCommandConfig
10
from strictdoc.core.project_config import ProjectConfig
11
 
12
 
13
@dataclass
14
class UvicornReloadConfig:
15
    """
16
    Class that encapsulate the Uvicorn server configuration details.
17
 
18
    Uvicorn has an opinionated system for configuring the reloading of a server
19
    when the specified files are changed on the file system. This class
20
    centralized the reload configuration details in one place.
21
 
22
    One responsibility of this class is to give a correct configuration when
23
    the files must be reloaded and give a completely empty configuration when
24
    the reload is False. In particular, if a configuration specifies reload=False
25
    but the reload_dirs or reload_includes or reload_excludes are specified,
26
    then the following warning is shown.
27
    WARNING:  Current configuration will not reload as not all conditions are met, please refer to documentation.
28
    The create() method of this class ensures that uvicorn is happy and the
29
    warning does not appear.
30
    """
31
 
32
    reload: bool
33
    reload_dirs: Optional[List[str]]
34
    reload_includes: Optional[List[str]]
35
    reload_excludes: Optional[List[str]]
36
 
37
    @classmethod
38
    def create(
39
        cls, project_config: ProjectConfig, server_config: ServerCommandConfig
40
    ) -> "UvicornReloadConfig":
41
        reload: bool = server_config.reload
42
        reload_dirs: Optional[List[str]] = None
43
        reload_includes: Optional[List[str]] = None
44
        reload_excludes: Optional[List[str]] = None
45
 
46
        if server_config.reload:
47
            # This doesn't seem to be effect because we still must provide the
48
            # reload_excludes outside this folder. Maybe it has to do with the
49
            # presence of reload_includes that includes all files with given
50
            # file extensions below. Anyway this works well enough with the
51
            # given reload_excludes.
52
            reload_dirs = [
53
                os.path.join(
54
                    project_config.get_strictdoc_root_path(), "strictdoc"
55
                )
56
            ]
57
            # It is important that StrictDoc's artifacts are excluded because
58
            # StrictDoc's web server will be triggered to restart every time the
59
            # contents of these folders changes.
60
            reload_excludes = (
61
                cls.expand_folder("build", max_depth=15)
62
                + cls.expand_folder("docs", max_depth=3)
63
                + cls.expand_folder("dist", max_depth=3)
64
                + cls.expand_folder("tests", max_depth=15)
65
                + cls.expand_folder("output", max_depth=15)
66
                + cls.expand_folder("Output", max_depth=15)
67
            )
68
 
69
            # Changing typical StrictDoc's own source code files should trigger
70
            # restarting of the web server. Whitelisting them here.
71
            reload_includes = [
72
                "*.py",
73
                "*.html",
74
                "*.jinja",
75
                "*.svg",
76
                "*.css",
77
                "*.js",
78
                "*.toml",
79
            ]
80
        return UvicornReloadConfig(
81
            reload=reload,
82
            reload_dirs=reload_dirs,
83
            reload_includes=reload_includes,
84
            reload_excludes=reload_excludes,
85
        )
86
 
87
    @classmethod
88
    def expand_folder(cls, folder: str, max_depth: int) -> List[str]:
89
        """
90
        Create a list of wildcard-based paths for a given folder.
91
 
92
        It looks like the regex engine of the uvicorn file watching library
93
        does not support proper ** globs.
94
 
95
        Examples:
96
        "tests",  # Doesn't work.
97
        "tests/",  # Doesn't work.
98
        "tests/*",  # Doesn't work.
99
        "tests/**",  # Makes the process hang, server doesn't start.
100
 
101
        Example of a possible StrictDoc path that must be excluded:
102
        output/cache/server/html/_source_files/strictdoc/backend/reqif/sdoc_reqif_fields.py.html
103
 
104
        This function produces output like this:
105
        [
106
            "output/*.*",
107
            "output/*/*.*",
108
            "output/*/*/*.*",
109
            "output/*/*/*/*.*",
110
            "output/*/*/*/*/*.*",
111
            "output/*/*/*/*/*/*.*",
112
            "output/*/*/*/*/*/*/*.*",
113
            "output/*/*/*/*/*/*/*/*.*",
114
            "output/*/*/*/*/*/*/*/*/*.*",
115
            "output/*/*/*/*/*/*/*/*/*/*.*",
116
            "output/*/*/*/*/*/*/*/*/*/*/*.*",
117
            "output/*/*/*/*/*/*/*/*/*/*/*/*.*",
118
            "output/*/*/*/*/*/*/*/*/*/*/*/*/*.*",
119
            "output/*/*/*/*/*/*/*/*/*/*/*/*/*/*.*",
120
            "output/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*.*",
121
        ]
122
        """
123
 
124
        patterns = []
125
        for depth in range(1, max_depth + 1):
126
            pattern = folder + "/" + "*/" * (depth - 1) + "*.*"
127
            patterns.append(pattern)
128
        return patterns