Module pdoc3_mdnotes.mdnotes
mdnotes
Contains all necessary functions, classes and methods to use extension. It can
be run as standalone script. Then html
notes will be created from files
placed in script directory ('templates' folder can be also added there for
customization). main()
function creates tempfile.TemporaryDirectory
and
places there .md
files content enclosed with docstring converted to .py
.
Directory structure is preserved. All README.md
are renamed to __init__.py
which helps build index.html
page for each folder with notes. Modules used:
os
, pathlib
, sys
, tempfile
and pdoc
.
License
Code interacting with pdoc is under GNU AGPL-3.0+ License. To the rest of program apply MIT License and below statement:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Expand source code Browse git
#!/usr/bin/env python3
"""#### mdnotes
Contains all necessary functions, classes and methods to use extension. It can
be run as standalone script. Then `html` notes will be created from files
placed in script directory ('templates' folder can be also added there for
customization). `main` function creates `tempfile.TemporaryDirectory` and
places there `.md` files content enclosed with docstring converted to `.py`.
Directory structure is preserved. All `README.md` are renamed to `__init__.py`
which helps build `index.html` page for each folder with notes. Modules used:
`os`, `pathlib`, `sys`, `tempfile` and `pdoc`.
#### License
Code interacting with [pdoc](https://pdoc3.github.io/pdoc/) is under [GNU
AGPL-3.0+](https://raw.githubusercontent.com/pdoc3/pdoc/master/LICENSE.txt)
License. To the rest of program apply MIT License and below statement:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
from pathlib import Path
import sys
from tempfile import TemporaryDirectory
import pdoc
ACCESS_ERRORS = (AttributeError, FileNotFoundError,
NotADirectoryError, PermissionError)
def load(path: Path) -> str:
"""Return file content from set path."""
with open(path, "r") as data:
return data.read()
def save(path: Path, content: str):
"""Save passed content to file located in set path."""
with open(path, "w") as data:
data.write(content)
class Converter:
"""
Class responsible for `.md` files conversion to `.py`.
It preserves original directory structure.
...
Attributes
----------
path : pathlib.Path
project path leading to directory containing `.md` files to convert
files : list
containing paths to collected files
directories : list
containing paths to collected directories
"""
def __init__(self, path):
"""Collect `.md` files and directories paths from set location."""
self.path = path
self.files = []
self.directories = []
self.collect(path)
def convert(self, directory):
"""Convert all collected files from `.md` to `.py`.
If file name is `README.md` change it to `__init__.py`. Each file is
saved to passed location preserving its relative path to project
directory.
Parameters
----------
directory : pathlib.Path
path to directory where converted files will be stored
"""
self.create_structure(directory)
for path in self.files:
if path.suffix == ".md":
if path.stem.upper() == "README":
name = "__init__.py"
else:
name = path.stem + ".py"
data = '"""\n' + load(path) + '\n"""'
path = directory / path.relative_to(self.path.parent).parent
save(path / name, data)
def collect(self, directory):
"""Collect all files from set directory.
Iterate through files in set directory and place them in
`self.directories` or `self.files`. When folder is met call self again
with that path. In case of `PermissionError` skip that location.
Parameters
----------
directory : pathlib.Path
directory from which paths will be collected
"""
for path in directory.iterdir():
try:
if path.is_dir():
self.directories.append(path)
self.collect(path)
else:
if path.suffix == ".md":
self.files.append(path)
except PermissionError:
pass
def create_structure(self, directory):
"""Reproduce project directory structure in set location.
Parameters
----------
directory : pathlib.Path
path to directory where converted files will be stored
"""
self.create_directory(self.path, directory)
for path in self.directories:
self.create_directory(path, directory)
def create_directory(self, path, destination):
"""Reproduce directory from set path in destination.
Parameters
----------
path : pathlib.Path
directory which will be reproduced
destination : pathlib.Path
location where to reproduce directory
"""
path = destination / path.relative_to(self.path.parent)
path.mkdir(parents=True, exist_ok=True)
class Notes:
"""
Class interacting with `pdoc` to create `html` notes.
...
Attributes
----------
destination : pathlib.Path
path to project directory where folder from `self.name` will be created
name : str, optional
name of directory where `html` files will be stored (default is "docs")
module : pdoc.Module
`pdoc` object representing module from which notes will be generated
"""
def __init__(self, path, destination, name="docs", templates=None):
"""Set notes templates. Initialize `pdoc` linker.
Parameters
----------
path : pathlib.Path
path to directory containing `.py` files with notes
templates : pathlib.Path or None, optional
path to directory containing customized templates which will be
used to create `html` notes (default is `None`)
"""
self.destination = destination
self.name = name
self.set_templates(templates)
context = pdoc.Context()
source = str(path / destination.name)
self.module = pdoc.Module(source, context=context)
pdoc.link_inheritance(context)
def generate(self):
"""Create `html` notes in `self.destination`/`self.name` directory."""
for path, content in self.get(self.module):
relative = "/".join(Path(path).parts[1:])
path = self.destination / self.name / relative
path.parent.mkdir(parents=True, exist_ok=True)
save(path, content)
def get(self, module):
"""Obtain `url` and `html` content from set module and its submodules.
Paramters
---------
module : pdoc.Module
object from which `url` and `html` content will be obtained, can
contain submodules
Yields
------
tuple
containing module `url` and its `html` content
"""
yield module.url(), module.html()
for submodule in module.submodules():
yield from self.get(submodule)
def set_templates(self, directory):
"""Set `pdoc` templates according to available options.
When passed path contains templates then they are used. If not folder
`templates` in `self.destination` is checked. If still nothing is
found templates from installation directory are taken. If it could not
find them anywhere it does not modify anything - templates provided
with `pdoc` are used.
Parameters
----------
directory : pathlib.Path or None
path to directory containing templates, `None` when not specified
"""
destination = self.destination / "templates"
module_dir = getattr(sys, "_MEIPASS", Path(__file__).parent.absolute())
module = Path(module_dir) / "templates"
for path in (directory, destination, module):
if self.change_templates(path):
break
def change_templates(self, path):
"""Change `pdoc` templates if set `path` contains all necessary files.
Parameters
----------
path : pathlib.Path or None
path to directory containing templates, `None` when not specified
Returns
-------
bool
information if templates were changed or not
"""
if self.has_templates(path):
pdoc.tpl_lookup.directories = [str(path)]
return True
return False
@staticmethod
def has_templates(directory):
"""Check if templates files are stored in set `directory`.
Parameters
----------
directory : pathlib.Path
path to directory which will be checked if templates are there
Returns
-------
bool
information if folder contains templates or not
"""
templates = ("config", "credits", "css", "head", "html", "logo")
try:
files = set(path.name for path in directory.iterdir())
expected = set(f"{name}.mako" for name in templates)
return not expected - files
except ACCESS_ERRORS:
return False
def main(path, *args, **kwargs):
"""Generate notes in `html` format.
Create `tempfile.TemporaryDirectory`. Then convert `.md` files from set
path to `.py` and store them in that directory. Finally generate `html`
notes from those files.
Parameters
----------
path : pathlib.Path
path to directory with notes
*args
optional arguments passed to `Notes` class
"""
with TemporaryDirectory() as temp_dir:
temp = Path(temp_dir)
converter = Converter(path)
converter.convert(temp)
notes = Notes(temp, path, *args, **kwargs)
notes.generate()
if __name__ == "__main__":
main(Path().absolute())
Functions
def load(path)
-
Return file content from set path.
Expand source code Browse git
def load(path: Path) -> str: """Return file content from set path.""" with open(path, "r") as data: return data.read()
def main(path, *args, **kwargs)
-
Generate notes in
html
format.Create
tempfile.TemporaryDirectory
. Then convert.md
files from set path to.py
and store them in that directory. Finally generatehtml
notes from those files.Parameters
path
:pathlib.Path
- path to directory with notes
*args
- optional arguments passed to
Notes
class
Expand source code Browse git
def main(path, *args, **kwargs): """Generate notes in `html` format. Create `tempfile.TemporaryDirectory`. Then convert `.md` files from set path to `.py` and store them in that directory. Finally generate `html` notes from those files. Parameters ---------- path : pathlib.Path path to directory with notes *args optional arguments passed to `Notes` class """ with TemporaryDirectory() as temp_dir: temp = Path(temp_dir) converter = Converter(path) converter.convert(temp) notes = Notes(temp, path, *args, **kwargs) notes.generate()
def save(path, content)
-
Save passed content to file located in set path.
Expand source code Browse git
def save(path: Path, content: str): """Save passed content to file located in set path.""" with open(path, "w") as data: data.write(content)
Classes
class Converter (path)
-
Class responsible for
.md
files conversion to.py
.It preserves original directory structure.
…
Attributes
path
:pathlib.Path
- project path leading to directory containing
.md
files to convert files
:list
- containing paths to collected files
directories
:list
- containing paths to collected directories
Collect
.md
files and directories paths from set location.Expand source code Browse git
class Converter: """ Class responsible for `.md` files conversion to `.py`. It preserves original directory structure. ... Attributes ---------- path : pathlib.Path project path leading to directory containing `.md` files to convert files : list containing paths to collected files directories : list containing paths to collected directories """ def __init__(self, path): """Collect `.md` files and directories paths from set location.""" self.path = path self.files = [] self.directories = [] self.collect(path) def convert(self, directory): """Convert all collected files from `.md` to `.py`. If file name is `README.md` change it to `__init__.py`. Each file is saved to passed location preserving its relative path to project directory. Parameters ---------- directory : pathlib.Path path to directory where converted files will be stored """ self.create_structure(directory) for path in self.files: if path.suffix == ".md": if path.stem.upper() == "README": name = "__init__.py" else: name = path.stem + ".py" data = '"""\n' + load(path) + '\n"""' path = directory / path.relative_to(self.path.parent).parent save(path / name, data) def collect(self, directory): """Collect all files from set directory. Iterate through files in set directory and place them in `self.directories` or `self.files`. When folder is met call self again with that path. In case of `PermissionError` skip that location. Parameters ---------- directory : pathlib.Path directory from which paths will be collected """ for path in directory.iterdir(): try: if path.is_dir(): self.directories.append(path) self.collect(path) else: if path.suffix == ".md": self.files.append(path) except PermissionError: pass def create_structure(self, directory): """Reproduce project directory structure in set location. Parameters ---------- directory : pathlib.Path path to directory where converted files will be stored """ self.create_directory(self.path, directory) for path in self.directories: self.create_directory(path, directory) def create_directory(self, path, destination): """Reproduce directory from set path in destination. Parameters ---------- path : pathlib.Path directory which will be reproduced destination : pathlib.Path location where to reproduce directory """ path = destination / path.relative_to(self.path.parent) path.mkdir(parents=True, exist_ok=True)
Methods
def collect(self, directory)
-
Collect all files from set directory.
Iterate through files in set directory and place them in
self.directories
orself.files
. When folder is met call self again with that path. In case ofPermissionError
skip that location.Parameters
directory
:pathlib.Path
- directory from which paths will be collected
Expand source code Browse git
def collect(self, directory): """Collect all files from set directory. Iterate through files in set directory and place them in `self.directories` or `self.files`. When folder is met call self again with that path. In case of `PermissionError` skip that location. Parameters ---------- directory : pathlib.Path directory from which paths will be collected """ for path in directory.iterdir(): try: if path.is_dir(): self.directories.append(path) self.collect(path) else: if path.suffix == ".md": self.files.append(path) except PermissionError: pass
def convert(self, directory)
-
Convert all collected files from
.md
to.py
.If file name is
README.md
change it to__init__.py
. Each file is saved to passed location preserving its relative path to project directory.Parameters
directory
:pathlib.Path
- path to directory where converted files will be stored
Expand source code Browse git
def convert(self, directory): """Convert all collected files from `.md` to `.py`. If file name is `README.md` change it to `__init__.py`. Each file is saved to passed location preserving its relative path to project directory. Parameters ---------- directory : pathlib.Path path to directory where converted files will be stored """ self.create_structure(directory) for path in self.files: if path.suffix == ".md": if path.stem.upper() == "README": name = "__init__.py" else: name = path.stem + ".py" data = '"""\n' + load(path) + '\n"""' path = directory / path.relative_to(self.path.parent).parent save(path / name, data)
def create_directory(self, path, destination)
-
Reproduce directory from set path in destination.
Parameters
path
:pathlib.Path
- directory which will be reproduced
destination
:pathlib.Path
- location where to reproduce directory
Expand source code Browse git
def create_directory(self, path, destination): """Reproduce directory from set path in destination. Parameters ---------- path : pathlib.Path directory which will be reproduced destination : pathlib.Path location where to reproduce directory """ path = destination / path.relative_to(self.path.parent) path.mkdir(parents=True, exist_ok=True)
def create_structure(self, directory)
-
Reproduce project directory structure in set location.
Parameters
directory
:pathlib.Path
- path to directory where converted files will be stored
Expand source code Browse git
def create_structure(self, directory): """Reproduce project directory structure in set location. Parameters ---------- directory : pathlib.Path path to directory where converted files will be stored """ self.create_directory(self.path, directory) for path in self.directories: self.create_directory(path, directory)
class Notes (path, destination, name='docs', templates=None)
-
Class interacting with
pdoc
to createhtml
notes.…
Attributes
destination
:pathlib.Path
- path to project directory where folder from
self.name
will be created name
:str
, optional- name of directory where
html
files will be stored (default is "docs") module
:pdoc.Module
pdoc
object representing module from which notes will be generated
Set notes templates. Initialize
pdoc
linker.Parameters
path
:pathlib.Path
- path to directory containing
.py
files with notes templates
:pathlib.Path
orNone
, optional- path to directory containing customized templates which will be
used to create
html
notes (default isNone
)
Expand source code Browse git
class Notes: """ Class interacting with `pdoc` to create `html` notes. ... Attributes ---------- destination : pathlib.Path path to project directory where folder from `self.name` will be created name : str, optional name of directory where `html` files will be stored (default is "docs") module : pdoc.Module `pdoc` object representing module from which notes will be generated """ def __init__(self, path, destination, name="docs", templates=None): """Set notes templates. Initialize `pdoc` linker. Parameters ---------- path : pathlib.Path path to directory containing `.py` files with notes templates : pathlib.Path or None, optional path to directory containing customized templates which will be used to create `html` notes (default is `None`) """ self.destination = destination self.name = name self.set_templates(templates) context = pdoc.Context() source = str(path / destination.name) self.module = pdoc.Module(source, context=context) pdoc.link_inheritance(context) def generate(self): """Create `html` notes in `self.destination`/`self.name` directory.""" for path, content in self.get(self.module): relative = "/".join(Path(path).parts[1:]) path = self.destination / self.name / relative path.parent.mkdir(parents=True, exist_ok=True) save(path, content) def get(self, module): """Obtain `url` and `html` content from set module and its submodules. Paramters --------- module : pdoc.Module object from which `url` and `html` content will be obtained, can contain submodules Yields ------ tuple containing module `url` and its `html` content """ yield module.url(), module.html() for submodule in module.submodules(): yield from self.get(submodule) def set_templates(self, directory): """Set `pdoc` templates according to available options. When passed path contains templates then they are used. If not folder `templates` in `self.destination` is checked. If still nothing is found templates from installation directory are taken. If it could not find them anywhere it does not modify anything - templates provided with `pdoc` are used. Parameters ---------- directory : pathlib.Path or None path to directory containing templates, `None` when not specified """ destination = self.destination / "templates" module_dir = getattr(sys, "_MEIPASS", Path(__file__).parent.absolute()) module = Path(module_dir) / "templates" for path in (directory, destination, module): if self.change_templates(path): break def change_templates(self, path): """Change `pdoc` templates if set `path` contains all necessary files. Parameters ---------- path : pathlib.Path or None path to directory containing templates, `None` when not specified Returns ------- bool information if templates were changed or not """ if self.has_templates(path): pdoc.tpl_lookup.directories = [str(path)] return True return False @staticmethod def has_templates(directory): """Check if templates files are stored in set `directory`. Parameters ---------- directory : pathlib.Path path to directory which will be checked if templates are there Returns ------- bool information if folder contains templates or not """ templates = ("config", "credits", "css", "head", "html", "logo") try: files = set(path.name for path in directory.iterdir()) expected = set(f"{name}.mako" for name in templates) return not expected - files except ACCESS_ERRORS: return False
Static methods
def has_templates(directory)
-
Check if templates files are stored in set
directory
.Parameters
directory
:pathlib.Path
- path to directory which will be checked if templates are there
Returns
bool
- information if folder contains templates or not
Expand source code Browse git
@staticmethod def has_templates(directory): """Check if templates files are stored in set `directory`. Parameters ---------- directory : pathlib.Path path to directory which will be checked if templates are there Returns ------- bool information if folder contains templates or not """ templates = ("config", "credits", "css", "head", "html", "logo") try: files = set(path.name for path in directory.iterdir()) expected = set(f"{name}.mako" for name in templates) return not expected - files except ACCESS_ERRORS: return False
Methods
def change_templates(self, path)
-
Change
pdoc
templates if setpath
contains all necessary files.Parameters
path
:pathlib.Path
orNone
- path to directory containing templates,
None
when not specified
Returns
bool
- information if templates were changed or not
Expand source code Browse git
def change_templates(self, path): """Change `pdoc` templates if set `path` contains all necessary files. Parameters ---------- path : pathlib.Path or None path to directory containing templates, `None` when not specified Returns ------- bool information if templates were changed or not """ if self.has_templates(path): pdoc.tpl_lookup.directories = [str(path)] return True return False
def generate(self)
-
Create
html
notes inself.destination
/self.name
directory.Expand source code Browse git
def generate(self): """Create `html` notes in `self.destination`/`self.name` directory.""" for path, content in self.get(self.module): relative = "/".join(Path(path).parts[1:]) path = self.destination / self.name / relative path.parent.mkdir(parents=True, exist_ok=True) save(path, content)
def get(self, module)
-
Obtain
url
andhtml
content from set module and its submodules.Paramters
module
:pdoc.Module
- object from which
url
andhtml
content will be obtained, can contain submodules
Yields
tuple
- containing module
url
and itshtml
content
Expand source code Browse git
def get(self, module): """Obtain `url` and `html` content from set module and its submodules. Paramters --------- module : pdoc.Module object from which `url` and `html` content will be obtained, can contain submodules Yields ------ tuple containing module `url` and its `html` content """ yield module.url(), module.html() for submodule in module.submodules(): yield from self.get(submodule)
def set_templates(self, directory)
-
Set
pdoc
templates according to available options.When passed path contains templates then they are used. If not folder
templates
inself.destination
is checked. If still nothing is found templates from installation directory are taken. If it could not find them anywhere it does not modify anything - templates provided withpdoc
are used.Parameters
directory
:pathlib.Path
orNone
- path to directory containing templates,
None
when not specified
Expand source code Browse git
def set_templates(self, directory): """Set `pdoc` templates according to available options. When passed path contains templates then they are used. If not folder `templates` in `self.destination` is checked. If still nothing is found templates from installation directory are taken. If it could not find them anywhere it does not modify anything - templates provided with `pdoc` are used. Parameters ---------- directory : pathlib.Path or None path to directory containing templates, `None` when not specified """ destination = self.destination / "templates" module_dir = getattr(sys, "_MEIPASS", Path(__file__).parent.absolute()) module = Path(module_dir) / "templates" for path in (directory, destination, module): if self.change_templates(path): break