Skip to content

utils

PathLibEnv

Bases: Environment

Subclass of jinja2.Environment to enable using pathlib.Path for template names.

Source code in cassini/utils.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class PathLibEnv(jinja2.Environment):
    """
    Subclass of `jinja2.Environment` to enable using `pathlib.Path` for template names.
    """

    def get_template(
        self,
        name: Union[Path, str],  # type: ignore[override]
        parent: Union[str, None] = None,
        globals: Union[MutableMapping[str, Any], None] = None,
    ) -> jinja2.Template:
        return super().get_template(
            name.as_posix() if isinstance(name, Path) else name,
            parent=parent,
            globals=globals,
        )

CassiniLabApp

Bases: LabApp

Subclass of jupyterlab.labapp.LabApp that ensures ContentsManager.allow_hidden = True (needed for jupyter_cassini_server)

Source code in cassini/utils.py
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
class CassiniLabApp(LabApp):  # type: ignore[misc]
    """
    Subclass of `jupyterlab.labapp.LabApp` that ensures `ContentsManager.allow_hidden = True`
    (needed for jupyter_cassini_server)
    """

    @classmethod
    def initialize_server(
        cls: Type[LabApp], argv: Union[Any, None] = None
    ) -> LabServerApp:
        """
        Patch serverapp to ensure hidden files are allowed, needed for jupyter_cassini_server
        """
        serverapp: LabServerApp = super().initialize_server(argv)
        serverapp.contents_manager.allow_hidden = True
        return serverapp

initialize_server classmethod

initialize_server(argv=None)

Patch serverapp to ensure hidden files are allowed, needed for jupyter_cassini_server

Source code in cassini/utils.py
50
51
52
53
54
55
56
57
58
59
@classmethod
def initialize_server(
    cls: Type[LabApp], argv: Union[Any, None] = None
) -> LabServerApp:
    """
    Patch serverapp to ensure hidden files are allowed, needed for jupyter_cassini_server
    """
    serverapp: LabServerApp = super().initialize_server(argv)
    serverapp.contents_manager.allow_hidden = True
    return serverapp

FileMaker

Utility content manager for making files, and then rolling back if an exception occurs.

Example
with FileMaker() as maker:
    maker.mkdir('mydir')
    raise Exception()

# prints:
# Exception occured, rolling back
# Removing mydir
# Done
Source code in cassini/utils.py
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
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
class FileMaker:
    """
    Utility content manager for making files, and then rolling back if an exception occurs.

    Example
    -------
    ```python
    with FileMaker() as maker:
        maker.mkdir('mydir')
        raise Exception()

    # prints:
    # Exception occured, rolling back
    # Removing mydir
    # Done
    ```
    """

    def __init__(self) -> None:
        self.folders_made: List[Path] = []
        self.files_made: List[Path] = []

    def __enter__(self) -> Self:
        self.files_made = []
        self.folders_made = []
        return self

    def mkdir(self, path: Path, exist_ok: bool = False) -> Union[Path, None]:
        """
        Make a directory.
        """
        if not path.exists():
            path.mkdir()
            self.files_made.append(path)
            return path
        if exist_ok:
            return None
        raise FileExistsError(path)

    def write_file(
        self, path: Path, contents: str = "", exist_ok: bool = False
    ) -> Union[Path, None]:
        """
        Write contents to a file.
        """
        if not path.exists():
            path.write_text(contents, encoding="utf-8")
            self.files_made.append(path)
            return path
        if exist_ok:
            return None
        raise FileExistsError(path)

    def copy_file(self, source: Path, dest: Path) -> Tuple[Path, Path]:
        """
        Copy a file.
        """
        self.write_file(dest, source.read_text())
        return source, dest

    def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
        if exc_type:
            print(f"{exc_type} occured, rolling back")
            for file in self.files_made:
                print("Deleting", file)
                file.unlink()
                print("Done")

            for folder in self.folders_made:
                print("Removing", folder)
                folder.rmdir()
                print("Done")
            raise exc_type(exc_val)

mkdir

mkdir(path, exist_ok=False)

Make a directory.

Source code in cassini/utils.py
89
90
91
92
93
94
95
96
97
98
99
def mkdir(self, path: Path, exist_ok: bool = False) -> Union[Path, None]:
    """
    Make a directory.
    """
    if not path.exists():
        path.mkdir()
        self.files_made.append(path)
        return path
    if exist_ok:
        return None
    raise FileExistsError(path)

write_file

write_file(path, contents='', exist_ok=False)

Write contents to a file.

Source code in cassini/utils.py
101
102
103
104
105
106
107
108
109
110
111
112
113
def write_file(
    self, path: Path, contents: str = "", exist_ok: bool = False
) -> Union[Path, None]:
    """
    Write contents to a file.
    """
    if not path.exists():
        path.write_text(contents, encoding="utf-8")
        self.files_made.append(path)
        return path
    if exist_ok:
        return None
    raise FileExistsError(path)

copy_file

copy_file(source, dest)

Copy a file.

Source code in cassini/utils.py
115
116
117
118
119
120
def copy_file(self, source: Path, dest: Path) -> Tuple[Path, Path]:
    """
    Copy a file.
    """
    self.write_file(dest, source.read_text())
    return source, dest

XPlatform

Bases: Generic[P, R]

Class for easily making cross platform functions/ methods(?). Stops nasty if elses.

Fallback functional called if no matching function added for current platform.

Parameters:

Name Type Description Default
default Callable[P, R]
required

Returns:

Name Type Description
self XPlatform[P, R]

Callable that will always call the appropriate function for the current platform.

Example

For example use, see open_file and win_open_file.

Source code in cassini/utils.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
class XPlatform(Generic[P, R]):
    """
    Class for easily making cross platform functions/ methods(?). Stops nasty if elses.

    Fallback functional called if no matching function added for current platform.

    Parameters
    ----------
    default : Callable[P, R]

    Returns
    -------
    self : XPlatform[P, R]
        Callable that will always call the appropriate function for the current platform.

    Example
    -------
    For example use, see [open_file][cassini.utils.open_file] and [win_open_file][cassini.utils.win_open_file].
    """

    def __init__(self, default: Callable[P, R]) -> None:
        self._func: Union[Callable[P, R], None] = None
        self._default: Callable[P, R] = default

    @property
    def func(self) -> Callable[P, R]:
        """
        Returns the platform appropriate function (or default if not known)
        """
        if self._func:
            return self._func

        return self._default

    def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
        return self.func(*args, **kwargs)

    def add(self, platform: str) -> Callable[[Callable[P, R]], "XPlatform[P, R]"]:
        """
        Add a platform dependent function. To be used as a decorator.

        Parameters
        ----------
        platform: str
            platform wrapped function called for.

        Returns
        -------
        self : XPlatform
            Callable that will always call the appropriate function for the current platform.
        """

        def wrapper(func: Callable[P, R]) -> "XPlatform[P, R]":
            if sys.platform == platform:
                self._func = func
                functools.update_wrapper(self, func)
            return self

        return wrapper

func property

func

Returns the platform appropriate function (or default if not known)

add

add(platform)

Add a platform dependent function. To be used as a decorator.

Parameters:

Name Type Description Default
platform str

platform wrapped function called for.

required

Returns:

Name Type Description
self XPlatform

Callable that will always call the appropriate function for the current platform.

Source code in cassini/utils.py
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
def add(self, platform: str) -> Callable[[Callable[P, R]], "XPlatform[P, R]"]:
    """
    Add a platform dependent function. To be used as a decorator.

    Parameters
    ----------
    platform: str
        platform wrapped function called for.

    Returns
    -------
    self : XPlatform
        Callable that will always call the appropriate function for the current platform.
    """

    def wrapper(func: Callable[P, R]) -> "XPlatform[P, R]":
        if sys.platform == platform:
            self._func = func
            functools.update_wrapper(self, func)
        return self

    return wrapper

open_file

open_file(filename)

Reveals a file in the file explorer.

*Nix implementation.

Source code in cassini/utils.py
202
203
204
205
206
207
208
209
210
211
212
@XPlatform
def open_file(filename: Union[str, Path]) -> None:
    """
    Reveals a file in the file explorer.

    *Nix implementation.
    """
    import subprocess

    opener = "open" if sys.platform == "darwin" else "xdg-open"
    subprocess.call([opener, filename])

win_open_file

win_open_file(filename)

Reveals a file in the file explorer.

Windows implementation.

Source code in cassini/utils.py
215
216
217
218
219
220
221
222
@open_file.add("win32")
def win_open_file(filename: Union[str, Path]) -> None:
    """
    Reveals a file in the file explorer.

    Windows implementation.
    """
    os.startfile(filename)  # type: ignore[attr-defined]

find_project

find_project(import_string=None)

Find the Project instance for this Python interpretter.

If this Python interpretter was launched via cassini.Project.launch(), this will already be set.

Otherwise, import_string or CASSINI_PROJECT environment variable can be set.

This should be of the form:

path/to/module.py:project_obj

By default, project_obj is assumed to be called project. This will be imported from module, which by default is assumed to be cas_project.

Note that for cassini to run with a regular jupyterlab instance, ContentsManager.allow_hidden = True must be set, either via a config, or passed as a command line argument e.g. --ContentsManager.allow_hidden=True

Returns:

Name Type Description
project Project

Found project instance. Note this also sets env.project.

Source code in cassini/utils.py
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
def find_project(import_string=None):
    """
    Find the Project instance for this Python interpretter.

    If this Python interpretter was launched via `cassini.Project.launch()`, this will already be set.

    Otherwise, `import_string` or CASSINI_PROJECT environment variable can be set.

    This should be of the form:

        path/to/module.py:project_obj

    By default, `project_obj` is assumed to be called `project`. This will be imported from `module`, which by default is
    assumed to be `cas_project`.

    Note that for cassini to run with a regular jupyterlab instance, `ContentsManager.allow_hidden = True` must be set, either
    via a config, or passed as a command line argument e.g. `--ContentsManager.allow_hidden=True`

    Returns
    -------
    project : Project
        Found project instance. Note this also sets `env.project`.
    """
    if env.project:
        return env.project

    if not import_string:
        CASSINI_PROJECT = os.environ["CASSINI_PROJECT"]
    else:
        CASSINI_PROJECT = import_string

    path = Path(CASSINI_PROJECT).absolute()

    module = None
    obj = None

    if ":" in path.name:
        module, obj = path.name.split(":")
        module = module.replace(".py", "")
        directory = path.parent.as_posix()
    elif path.is_file() or path.with_suffix(".py").is_file():
        directory = path.parent.as_posix()
        module = path.stem
        obj = "project"
    elif path.is_dir():
        directory = path.as_posix()
        module = "cas_project"
        obj = "project"
    else:
        raise RuntimeError(f"Cannot parse CASSINI_PROJECT {CASSINI_PROJECT}")

    sys.path.insert(0, directory)

    try:
        env.project = getattr(importlib.import_module(module), obj)
    finally:
        sys.path.remove(directory)

    return env.project