notesdir.accessors package

Submodules

notesdir.accessors.base module

Defines the API for parsing/changing information like metadata and links in individual files.

The most important class is Accessor.

class notesdir.accessors.base.Accessor(path: str)

Bases: object

Base class for accessors, which are responsible for reading and writing supported file types.

Each instance is for working with a single file, specified to the constructor.

path: str
edited: bool

If True, indicates the instance has unsaved edits for the file.

edit(edit: notesdir.models.FileEditCmd) → None

Applies the given change to this instance (but does not save it to the file yet).

May raise ChangeError. Should raise UnsupportedChangeError if the edit is unsupported for this file type or invalid for the file.

Raises ValueError if the path of the edit does not match the path of this accessor.

info()notesdir.models.FileInfo

Returns details about the file.

This will not necessarily reload the file from disk if the instance has previously loaded it.

This will only populate the attributes of FileInfo that are supported by the particular subclass, and also will not populate any attributes (such as backlinks) that cannot be derived from the file in isolation.

May raise ParseError.

load() → None

Attempts to parse the file. This does not normally need to be called explicitly.

It will be called by info() when necessary.

May raise ParseError.

save() → bool

Writes any changes from prior calls to edit() to the file.

Returns True if there were changes to save, and False if there were none. Raises ChangeError() or an IO-related exception if the changes cannot be saved.

This method may do nothing if self.edited is False.

This method may overwrite changes on disk that were made since the data was loaded.

exception notesdir.accessors.base.ChangeError(message: str, edits: List[notesdir.models.FileEditCmd], cause: Optional[BaseException] = None)

Bases: Exception

Raised when an Accessor is unable to perform a requested change.

class notesdir.accessors.base.MiscAccessor(path: str)

Bases: notesdir.accessors.base.Accessor

This accessor can be given the path to any file or folder, or even a nonexistent path.

Its info() method only populates the path field, and no editing or saving is supported.

exception notesdir.accessors.base.ParseError(message: str, path: str, cause: Optional[BaseException] = None)

Bases: Exception

Raised when an Accessor is unable to parse a file.

exception notesdir.accessors.base.UnsupportedChangeError(edit: notesdir.models.FileEditCmd)

Bases: notesdir.accessors.base.ChangeError

Raised when an Accessor does not support the type of change requested at all.

notesdir.accessors.delegating module

Provides the DelegatingAccessor class.

class notesdir.accessors.delegating.DelegatingAccessor(path: str)

Bases: notesdir.accessors.base.Accessor

Responsible for choosing what notesdir.accessors.base.Accessor subclass to use for a given file.

This selects an accessor based on the path’s file extension, and delegates method calls to that accessor.

Currently, the mapping is hardcoded:

  • .md -> MarkdownAccessor

  • .html -> HTMLAccessor

  • .pdf -> PDFAccessor

  • anything else -> MiscAccessor

edit(edit: notesdir.models.FileEditCmd)

Applies the given change to this instance (but does not save it to the file yet).

May raise ChangeError. Should raise UnsupportedChangeError if the edit is unsupported for this file type or invalid for the file.

Raises ValueError if the path of the edit does not match the path of this accessor.

info()notesdir.models.FileInfo

Returns details about the file.

This will not necessarily reload the file from disk if the instance has previously loaded it.

This will only populate the attributes of FileInfo that are supported by the particular subclass, and also will not populate any attributes (such as backlinks) that cannot be derived from the file in isolation.

May raise ParseError.

load()

Attempts to parse the file. This does not normally need to be called explicitly.

It will be called by info() when necessary.

May raise ParseError.

save() → bool

Writes any changes from prior calls to edit() to the file.

Returns True if there were changes to save, and False if there were none. Raises ChangeError() or an IO-related exception if the changes cannot be saved.

This method may do nothing if self.edited is False.

This method may overwrite changes on disk that were made since the data was loaded.

notesdir.accessors.html module

Provides the HTMLAccessor class.

class notesdir.accessors.html.HTMLAccessor(path: str)

Bases: notesdir.accessors.base.Accessor

Responsible for parsing and updating HTML files.

Current support:

  • Title is stored in the <title> element.

  • Creation date is stored in the <meta name="created"> element’s content attribute.

  • Tags are stored in the <meta name="keywords"> element’s content attribute, comma-separated.

  • Links can be recognized and updated when they are in the a, img, video, audio, or source elements. Note that the srcset attribute is not currently supported.

If the file does not at least contain an <html> element, attempting to add metadata will fail.

BeautifulSoup4 is used for parsing and updating the files; formatting may be changed during updates.

notesdir.accessors.markdown module

class notesdir.accessors.markdown.MarkdownAccessor(path: str)

Bases: notesdir.accessors.base.Accessor

Responsible for parsing and updating Markdown files.

Current support:

  • Metadata is stored in a YAML metadata header.

  • Tags can be stored in both the keywords YAML key and as hashtags in the body.
    • When this class needs to add a new tag, it will always do so in the YAML metadata.

    • When removing a tag, this class will delete any occurrences of the hashtag from the body, in addition to deleting from the YAML metadata.

    • Hashtags are only recognized when they are preceded by whitespace or begin the line. Hashtags must begin with a letter a-z and can only contain letters a-z and digits.

  • Links can be recognized and updated when they are in one of the following three formats:
    • [any link text](HREF)

    • ![any image title](HREF)

    • (at beginning of a line) [any id]: HREF optional text

Currently, parsing and updating is done via regex, so formatting changes should be minimal but false positives for links and hashtags are a risk.

Here’s an example Markdown file with metadata and hashtags:

---
title: My Boring Note
created: 2001-02-03 04:05:06
keywords:
- boring
- unnecessary
---

The three hyphens indicate the end of the metadata. Now we're in **Markdown**!
This is a really #uninteresting note.

notesdir.accessors.pdf module

class notesdir.accessors.pdf.PDFAccessor(path: str)

Bases: notesdir.accessors.base.Accessor

Responsible for parsing and updating PDF files.

Current support:

  • Metadata is stored in the PDF’s “document info”:
    • /Title

    • /CreationDate

    • /Keywords (comma-separated)

  • Links are not currently supported, so although notesdir can update links to PDF files from other files, it will not currently update links from PDF files to other files.

PyPDF4 is used for parsing and updating.

Note: when updating a PDF containing the metadata key /AAPL:Keywords (which is often included on PDFs generated on Mac), that field will be removed. It appears to violate version 1.7 of the PDF spec, and PyPDF4 cannot serialize it.

Module contents

Support for various file types.

notesdir.accessors.base.Accessor is the API that must be implemented to add support for a file type. The other modules in this package provide implementations for specific file types.