Package: rich-theme-manager
Rich Theme ManagerDescriptionImplements a basic "theme manager" class for managing richThemesin yourrichCLI application.The rich package provides an easy way to define custom styles and themes for use with rich.  This package provides a simple way to manage the themes: e.g. to list, add, remove themes, for the user to preview themes, and to manage themes on disk.What problem does this solve?Consider this scenario from the richdocumentation:If you re-use styles it can be a maintenance headache if you ever want to modify an attribute or color – you would have to change every line where the style is used. Rich provides a Theme class which you can use to define custom styles that you can refer to by name. That way you only need to update your styles in one place.Style themes can make your code more semantic, for instance a style called "warning" better expresses intent that "italic magenta underline".To use a style theme, construct a Theme instance and pass it to the Console constructor. Here’s an example:fromrich.consoleimportConsolefromrich.themeimportThemecustom_theme=Theme({"info":"dim cyan","warning":"magenta","danger":"bold red"})console=Console(theme=custom_theme)console.print("This is information",style="info")console.print("[warning]The pod bay doors are locked[/warning]")console.print("Something terrible happened!",style="danger")I highly recommend the use of Themes in your rich application instead of hard-coding colors and styles. However, there's still a problem of managing themes.  For example, letting the user of your application change the default theme styles you've chosen.  What if they are color blind and can't distinguish the colors you've selected? What if they're using a light terminal background and you've selected colors best suited for a dark terminal?The richThemeclass provides everything you to manage these situations but doing so requires a fair amount of boiler plate code for each application. This package attempts to provide an easy solution for these scenarios with a minimal amount of code.  Instead of having to implement management of theme files and allowing the user to list or preview themes yourself, you can use theThemeManagerclass to manage themes for you.SynopsisUsingrich_theme_manageris easy and takes just a few lines of code.  ImportThemefromrich_theme_managerinstead of fromrich.theme(rich_theme_manager.Themesubclassesrich.theme.Theme) then useThemeManagerto manage themes.ThemeManagercan be created with or without atheme_dirargument. If you don't providetheme_dir,ThemeManagerwill not manage themes on disk.  This may still be useful for allowing the user to list and preview themes.  If you do providetheme_dir, any default themes passed toThemeManagerwill be written totheme_dirand any theme config files found intheme_dirwill be loaded.fromrich.consoleimportConsolefromrich.styleimportStyleimportpathlibfromrich_theme_managerimportTheme,ThemeManagerTHEMES=[Theme(name="dark",description="Dark mode theme",tags=["dark"],styles={"info":"dim cyan","warning":"bold magenta","danger":"bold red",},),Theme(name="light",description="Light mode theme",styles={"info":Style(color="#22863a",bold=True),"warning":Style(color="#032f62",bold=True),"danger":Style(color="#b31d28",bold=True,underline=True,italic=True),},),Theme(name="mono",description="Monochromatic theme",tags=["mono","colorblind"],styles={"info":"italic","warning":"bold","danger":"reverse bold",},),]if__name__=="__main__":# you can specify a config directory to save/load themes to/fromtheme_dir=pathlib.Path("~/.rich_theme_manager/themes").expanduser()theme_dir.expanduser().mkdir(parents=True,exist_ok=True)theme_manager=ThemeManager(theme_dir=theme_dir,themes=THEMES)theme_manager.list_themes()print("\n")dark=theme_manager.get("dark")theme_manager.preview_theme(dark)console=Console(theme=dark)print("\n")console.print("This is information",style="info")console.print("[warning]The pod bay doors are locked[/warning]")console.print("Something terrible happened!",style="danger")Example appA simple example app that demonstrates the ThemeManager class comes with rich_theme_manager in__main__.py:python -m rich_theme_manager:usage: rich_theme_manager [-h] [--example [EXAMPLE]] [--list] [--preview THEME] [--config THEME]

Example CLI usage of rich_theme_manager

optional arguments:
  -h, --help           show this help message and exit
  --example [EXAMPLE]  Show example output for theme.
  --list               List themes.
  --preview THEME      Preview theme.
  --config THEME       Print configuration for theme THEME.python -m rich_theme_manager --list:python -m rich_theme_manager --preview dark:DocumentationTheme classrich_theme_manager.Themeis a subclass ofrich.theme.Themeand provides additional functionality for managing themes.Theme(name:str,description:Optional[str]=None,styles:Optional[Mapping[str,StyleType]]=None,inherit:bool=True,tags:Optional[List[str]]=None,path:Optional[str]=None,)Argumentsname: The name of the theme; requireddescription: An optional description of the theme.styles: An optional mapping of style names to styles.inherit: Whether the theme inherits from the default theme.tags: An optional list of tags for the theme; useful for allowing user to filter themes.path: The path to the theme file; in normal use this is not needed asThemeManagerwill automatically create the theme file.PropertiesTheme().name: The name of the themeTheme().description: Description of the themeTheme().tags: List of tags for the themeTheme().inherit: bool indicating whether the theme inherits from the default themeTheme().style_names: List of names for styles in the themeTheme().path: The path to the theme file; (getter/setter)Theme().config: Contents of a configuration file for the theme (same format asrich.theme.Theme().configbut with an additional[metadata]section)MethodsTheme().save(): Save the theme to disk (toTheme().path)Theme().load(): Load the theme from disk (fromTheme().path)Theme().to_file(path: str): Save the theme to disk (topath)Theme().update(other: Theme, overwrite_existing_styles: bool = True): Update the theme with the values from another theme, optionally overwriting existing styles.Class Methods:Theme.from_file(config_file: IO[str], source: Optional[str] = None, inherit: bool = True)->Theme: Load a theme from a text mode configuration file (inconfigparserINI format).Theme.read(path: str, inherit: bool = True) -> Theme: Load a theme from disk (frompath)The.themeINI file format looks like this:[metadata]name=darkdescription=Dark mode themetags=darkinherit=True[styles]danger=bold redinfo=dim cyanwarning=bold magentaHere's an real world example of a theme INI file from one of myapps:[metadata]name=darkdescription=Dark mode themetags=darkinherit=True[styles]bar.back=rgb(68,71,90)bar.complete=rgb(249,38,114)bar.finished=rgb(80,250,123)bar.pulse=rgb(98,114,164)color=rgb(248,248,242)count=rgb(139,233,253)error=bold rgb(255,85,85)filename=bold rgb(189,147,249)filepath=bold rgb(80,250,123)highlight=bold#000000 on #d73a49num=bold rgb(139,233,253)progress.elapsed=rgb(139,233,253)progress.percentage=rgb(255,121,198)progress.remaining=rgb(139,233,253)time=bold rgb(139,233,253)uuid=rgb(255,184,108)warning=bold rgb(241,250,140)Themeimplements therich Console protocolwhich means that you userich.print()andrich.console.Console().print()to print a theme to the console.  Doing so results in a preview of the theme which visually shows the colors and styles used in the theme.TheThemepreview will use default sample text for each style. You can change the sample text by setting therich_theme_manager.theme.SAMPLE_TEXTglobal variable.Themeimplements the__eq__method so twoThemeinstances can be easily compared for equality.Themeinstances are considered equal if all properties with exception ofpathare equal.ThemeManager classThemeManager(theme_dir:Optional[str]=None,themes:Optional[List[Theme]]=None,overwrite:bool=False,update:bool=False,)Arguments:theme_dir: Optional directory to save/load themes to/from.themes: Optional list of Theme objectsoverwrite: overwrite existing theme filesupdate: update existing theme files withnewstyles from themes but don't replaceexistingstylesIf provided,theme_dirmust exist.  Iftheme_diris set (for example, usingclick.get_app_dir), upon initializationThemeManagerwill save any default themes provided viathemestotheme_dirand load any themes fromtheme_dir.  Theme files are standard INI files as created byconfigparserand are named<name>.themewhere<name>is the name of the Theme (seeTheme.name).  If a theme file already exists, it will be loaded andThemeManagerwill not overwrite it unlessoverwrite=True.  Ifupdate=True, any new styles defined inthemeswill be added to the theme file but existing styles will not be replaced. If tags and description of themes are different andupdate=True, those items will also be updated in the theme file.  This allows you to add new styles to an existing theme in future app updates without overriding changes made by the user to theme styles.Properties:ThemeManager().themes: List of themesMethods:ThemeManager().add(theme: Theme, overwrite=False) -> None: Add a theme to the list of managed themes.  Ifoverwriteis True, the theme file will be overwritten if it already exists.ThemeManager().remove(theme: Theme) -> None: Remove a theme from the list of managed themes and delete the theme file if it exists.ThemeManager().get(theme_name: str) -> Theme: Get a theme by name. Raises 'ValueError` if no theme with the given name is found.ThemeManager().load_themes(theme_dir=None) -> None: Load themes fromtheme_dir(orThemeManager().theme_dirif not provided).  Any.themefiles found intheme_dirwill be loaded and added to the list of managed themes.ThemeManager().write_themes(overwrite=False) -> None: Write themes to file (as specified in eachTheme().pathwhich will be set automatically byThemeManager).  Ifoverwriteis True, the theme file will be overwritten if it already exists.ThemeManager().list_themes(show_path: bool = True, theme_names: Optional[List[str]] = None, console: Optional[Console] = None) -> None: Print a list of themes to the console.  Ifshow_pathis True, the path to the theme file will be printed.  Iftheme_namesis provided, only themes with names in the list will be printed. An optionalrich.console.Console()instance may be provided to print to a specific console.Class Methods:ThemeManager.preview_theme(theme: Theme, sample_text: Optional[str] = None, show_path: bool = True, console: Optional[Console] = None) -> None: Print a preview of the theme to the console showing the style of each style in the theme.  Ifsample_textis provided, it will be used as the sample text to preview otherwise a default string will be used.  Ifshow_pathis True, the path to the theme file will be printed.  An optionalrich.console.Console()instance may be provided to print to a specific console.Test Coverage100% coverage of all code with exception of the example CLI app.---------- coverage: platform darwin, python 3.8.12-final-0 ----------
Name                             Stmts   Miss  Cover
----------------------------------------------------
rich_theme_manager/__init__.py       5      0   100%
rich_theme_manager/manager.py       73      0   100%
rich_theme_manager/theme.py        155      0   100%
----------------------------------------------------
TOTAL                              233      0   100%LicenseMIT LicenseContributingContributions of all kinds are welcome!  Please submit pull requests, issues, and/or suggestions to thegithub repo.CreditsThank you toWill McGuganfor creatingrichand helping to make our command line interfaces more beautiful!Projects Using Rich Theme Managerosxphotos: Python app to export pictures and associated metadata from Apple Photos on macOS. Also includes a package to provide programmatic access to the Photos library, pictures, and metadata.