Compare commits

..

15 Commits

Author SHA1 Message Date
694d4b74a4 Ajoute un cache pour l'HTML généré 2025-07-27 16:49:31 +02:00
4c49d4e96a Ajoute le flux atom 2025-07-17 18:12:51 +02:00
c1b730d396 Merge branch 'main' into rss 2025-07-13 19:49:03 +02:00
4321429dbf Update makefile 2025-06-24 20:06:44 +02:00
8c4cb8dad2 Ajoute la gestion des brouillons 2025-06-24 19:58:56 +02:00
Florestan Bredow
ee19286ee8 WIP ajoute generation flux rss 2025-06-16 16:57:50 +02:00
945aa6e57b Ignore main.spec 2025-06-12 18:40:51 +02:00
241c44a380 Ajoute l'action install 2025-06-12 18:39:16 +02:00
Florestan Bredow
1e30909e52 WIP ajoute generation flux rss 2025-06-06 08:41:37 +02:00
Florestan Bredow
ca58f85738 Supprime le theme des sources 2025-06-06 08:39:10 +02:00
776e646f32 Supprime les outils (black, flake8…) des dépendances. 2025-06-04 19:34:11 +02:00
3bcb47769a Ajoute un makefile 2025-06-04 19:32:59 +02:00
Florestan Bredow
3c9c948fe2 Supprime code inutil 2025-06-03 18:02:42 +02:00
Florestan Bredow
73760341d9 Nettoie le code (flake8) 2025-06-03 17:40:47 +02:00
Florestan Bredow
e4b6b80fdb Passe black pour unifier le code 2025-06-03 17:37:55 +02:00
14 changed files with 163 additions and 325 deletions

View File

@@ -1,5 +1,11 @@
theme: './theme/default' author_name: 'Firstname Name'
author_mail: 'name@example.com'
draft: './draft'
# python -m uuid -u uuid4
id: 'a06cd9a6-3a48-479b-bf7f-40ddcdde7982'
inbox: './inbox' inbox: './inbox'
outbox: './outbox' theme: './theme/default'
title: 'Blog title' title: 'Blog title'
presentation: 'Blog presentation.' url: 'https://blog.example.com'
outbox: './outbox'
presentation: 'Blog presentation.'

3
.gitignore vendored
View File

@@ -5,6 +5,7 @@ build/
dist/ dist/
wheels/ wheels/
*.egg-info *.egg-info
main.spec
# Virtual environments # Virtual environments
.venv .venv
@@ -13,3 +14,5 @@ wheels/
.blog .blog
inbox/ inbox/
outbox/ outbox/
themes/
draft/

25
Makefile Normal file
View File

@@ -0,0 +1,25 @@
default: build
.PHONY: build install-tools install-dep init clean
init: install-dep install-tools
build:
@uvx pyinstaller --hidden-import=yaml --hidden-import=yaml.loader --paths=.venv/lib/python3.13/site-packages/ --onefile ./main.py
install-tools:
@uv tool install flake8
@uv tool install black
@uv tool install pyinstaller
install-dep:
@uv sync
clean:
@rm -r ./dist ./build
install:
ifeq ($(USER), root)
@cp ./dist/main /usr/local/bin/blog
else
@cp ./dist/main ~/.local/bin/blog
endif

View File

@@ -1,11 +1,12 @@
import glob import glob
import shutil import shutil
import datetime
import textwrap
from pathlib import Path from pathlib import Path
from blog.page import Page from blog.page import Page
from blog.config import Config from blog.config import Config
from jinja2 import Environment, FileSystemLoader, Template from jinja2 import Environment, FileSystemLoader, Template
class Blog: class Blog:
def __init__(self, conf: Config): def __init__(self, conf: Config):
@@ -13,18 +14,14 @@ class Blog:
self.conf = conf self.conf = conf
self.pages = dict() self.pages = dict()
def load_pages(self): def make(self, draft: bool = False):
"""Charge tous les fichiers .md dans le dossier inbox""" """Convertit les pages en un site html"""
files_list = glob.glob(f"{self.conf.inbox}/*.md")
self.pages = dict() self.pages = dict()
for file in files_list: self._load_pages(self.conf.inbox)
self.pages[Path(file).stem] = Page(Path(file))
def make(self): if draft:
"""Convertit les pages en un site html""" self._load_pages(self.conf.draft)
if not self.pages:
self.load_pages()
env = Environment(loader=FileSystemLoader(self.conf.theme)) env = Environment(loader=FileSystemLoader(self.conf.theme))
@@ -34,6 +31,13 @@ class Blog:
self._copy_css() self._copy_css()
self._build_all_pages(page_template) self._build_all_pages(page_template)
self._build_index(index_template) self._build_index(index_template)
self._build_atom()
def _load_pages(self, path: Path):
"""Charge tous les fichiers .md dans le dossier inbox"""
files_list = glob.glob(f"{path}/*.md")
for file in files_list:
self.pages[Path(file).stem] = Page(Path(file))
def _build_all_pages(self, template: Template): def _build_all_pages(self, template: Template):
"""Convertit les pages markdown dans conf.inbox en html dans conf.outbox""" """Convertit les pages markdown dans conf.inbox en html dans conf.outbox"""
@@ -43,7 +47,7 @@ class Blog:
outbox_path.mkdir() outbox_path.mkdir()
for filename in self.pages: for filename in self.pages:
html_content = self.pages[filename].html(template) html_content = self.pages[filename].html_template(template)
with open(f"{self.conf.outbox}/pages/{filename}.html", "w+") as html_file: with open(f"{self.conf.outbox}/pages/{filename}.html", "w+") as html_file:
html_file.write(html_content) html_file.write(html_content)
@@ -55,13 +59,58 @@ class Blog:
with open(f"{self.conf.outbox}/index.html", "w+") as html_file: with open(f"{self.conf.outbox}/index.html", "w+") as html_file:
html_file.write(html_content) html_file.write(html_content)
def _build_atom(self):
""" """
updated = datetime.date(1970, 1, 1)
articles = ""
for filename in self.pages:
date = self.pages[filename].date
if updated < date:
updated = date
articles += textwrap.indent(
textwrap.dedent(f"""\
<entry>
<title>{self.pages[filename].title}</title>
<link href="{self.conf.url}/pages/{filename}.html"/>
<updated>{date}T00:00:00Z</updated>
<id>urn:uuid:{self.pages[filename].id}</id>
<content type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml">
{self.pages[filename].html()}
</div>
</content>
</entry>
"""),
" ")
header = textwrap.dedent(f""" <?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>{self.conf.title}</title>
<subtitle>{self.conf.presentation}</subtitle>
<link href="{self.conf.url}/atom.xml" rel="self"/>
<link href="{self.conf.url}/index.html"/>
<updated>{updated}T00:00:00Z</updated>
<author>
<name>{self.conf.author_name}</name>
<email>{self.conf.author_mail}</email>
</author>
<id>urn:uuid:{self.conf.id}</id>
""")
footer = "</feed>"
with open(Path(self.conf.outbox) / "atom.xml", 'w+') as rss_file:
rss_file.write(header + articles + footer)
def _copy_css(self): def _copy_css(self):
css_path = Path(self.conf.theme) / 'css' """Copie les fichiers CSS du theme vers l'export"""
dest_path = Path(self.conf.outbox) / 'css' css_path = Path(self.conf.theme) / "css"
dest_path = Path(self.conf.outbox) / "css"
if not dest_path.exists(): if not dest_path.exists():
dest_path.mkdir() dest_path.mkdir()
for css_file in css_path.glob('*.css'): for css_file in css_path.glob("*.css"):
dest_file = dest_path / css_file.name dest_file = dest_path / css_file.name
shutil.copy(css_file, dest_file) shutil.copy(css_file, dest_file)

View File

@@ -5,7 +5,18 @@ from pathlib import Path
class Config: class Config:
_conf = dict() _conf = dict()
_list_valid_parameters = {"inbox", "outbox", "theme", "title", "presentation"} _list_valid_parameters = {
"inbox",
"outbox",
"theme",
"title",
"presentation",
"url",
"draft",
"author_name",
"author_mail",
"id"
}
def __init__(self, config_file: Path): def __init__(self, config_file: Path):
"""Constructeur : charge les paramètres depuis le fichier de configuration""" """Constructeur : charge les paramètres depuis le fichier de configuration"""

View File

@@ -1,34 +1,32 @@
import re import re
import os import os
import yaml import yaml
import uuid
import jinja2 import jinja2
import textwrap import textwrap
import markdown import markdown
import subprocess
from pathlib import Path from pathlib import Path
from datetime import date, datetime from datetime import date
# from bs4 import BeautifulSoup as bs
def new_page(title: str): def new_page(title: str, path: Path) -> Path:
"""Crée un nouvel article à partir du titre de celui ci.""" """Crée un nouvel article à partir du titre de celui ci."""
today = date.today().strftime("%Y-%m-%d") today = date.today().strftime("%Y-%m-%d")
filename = "./inbox/" + re.sub("[^a-zA-Z0-9-]", "_", title) + ".md" filename = path + "/" + re.sub("[^a-zA-Z0-9-]", "_", title) + ".md"
id = uuid.uuid4()
content = textwrap.dedent( content = textwrap.dedent(
f"""\ f"""\
--- ---
date: {today} date: {today}
title: {title} title: {title}
category: id: {id}
category:
tags: tags:
- -
--- ---
# {title}
""" """
) )
@@ -36,7 +34,7 @@ def new_page(title: str):
with open(filename, "w") as file: with open(filename, "w") as file:
file.write(content) file.write(content)
subprocess.run(["nvim", "+normal G$", "+startinsert", filename]) return filename
class Page: class Page:
@@ -45,6 +43,8 @@ class Page:
md_content = "" md_content = ""
filename = "" filename = ""
_cache = ""
def __init__(self, filename: Path): def __init__(self, filename: Path):
"""Constructeur : nouvelle page""" """Constructeur : nouvelle page"""
self.filename = filename self.filename = filename
@@ -59,13 +59,10 @@ class Page:
if "date" not in self.meta or type(self.meta["date"]) is not date: if "date" not in self.meta or type(self.meta["date"]) is not date:
self.meta["date"] = date(1970, 1, 1) self.meta["date"] = date(1970, 1, 1)
def __getattr__(self, name): def __getattr__(self, name: str):
if name not in self.meta: if name not in self.meta:
raise AttributeError(f"L'attribut '{name}' n'existe pas.") raise AttributeError(f"L'attribut '{name}' n'existe pas.")
# if name == date:
# return datetime(self.meta['date'], "%Y-%m-%d")
return self.meta[name] return self.meta[name]
def _extract_meta_and_markdown(self, content: str) -> list: def _extract_meta_and_markdown(self, content: str) -> list:
@@ -87,14 +84,21 @@ class Page:
meta += line + "\n" meta += line + "\n"
return meta, markdown return meta, markdown
def html(self, template: jinja2.Template) -> str: def html(self) -> str:
"""Convertit le markdown en html"""
if not self._cache:
self._cache = markdown.markdown(
self.md_content, extensions=["codehilite", "fenced_code"]
)
return self._cache
def html_template(self, template: jinja2.Template) -> str:
"""Convertit la page en html a partir d'un template jinja2""" """Convertit la page en html a partir d'un template jinja2"""
title = self.meta["title"] or "Sans titre" title = self.meta["title"] or "Sans titre"
date = self.meta["date"] or None date = self.meta["date"] or None
return template.render( return template.render(
title=title, title=title,
date=date, date=date,
content=markdown.markdown( content=self.html(),
self.md_content, extensions=["codehilite", "fenced_code"]
),
) )

View File

@@ -1,6 +0,0 @@
from pathlib import Path
def clean_output(path: Path):
# TODO
pass

44
main.py
View File

@@ -2,15 +2,15 @@
import os import os
import argparse import argparse
import subprocess
from pathlib import Path from pathlib import Path
from blog.page import new_page from blog.page import new_page
from blog.config import Config from blog.config import Config
from blog.utils import clean_output
from blog.blog import Blog from blog.blog import Blog
# from blog.blog import Blog # from blog.blog import Blog
actions_list = ["new", "make", "clean"] actions_list = ["new", "make"]
blog_dir = "./inbox/" blog_dir = "./inbox/"
@@ -19,35 +19,42 @@ def load_args():
parser = argparse.ArgumentParser(description="Gestion du blog") parser = argparse.ArgumentParser(description="Gestion du blog")
parser.add_argument("action", choices=actions_list, help="L'action a réaliser.") parser.add_argument("action", choices=actions_list, help="L'action a réaliser.")
parser.add_argument( parser.add_argument(
"-c",
"--config", "--config",
default=".blog", default=".blog",
help="Chemin vers le fichier de configuration", help="Chemin vers le fichier de configuration",
) )
parser.add_argument( parser.add_argument(
"-i",
"--inbox", "--inbox",
default="./inbox", default="./inbox",
help="Chemin vers les fichiers markdown du blog", help="Chemin vers les fichiers markdown du blog",
) )
parser.add_argument("all", nargs=argparse.REMAINDER) parser.add_argument(
"-d",
action="store_true",
help="Construit le blog avec les brouillons si spécifié.",
)
parser.add_argument(
"--draft",
default="./draft",
help="Chemin vers les brouillons",
)
parser.add_argument("all", nargs=argparse.REMAINDER, help=argparse.SUPPRESS)
return vars(parser.parse_args()) return vars(parser.parse_args())
def load_make_args(args: str) -> dict: def load_make_args(args: str) -> dict:
"""Charge les paramêtres spécifiques à l'action make""" """Charge les paramêtres spécifiques à l'action make"""
parser = argparse.ArgumentParser(description="Compile le blog") parser = argparse.ArgumentParser(description="Compile le blog")
parser.add_argument( parser.add_argument(
"-t",
"--theme", "--theme",
default="./themes/default", default="./themes/default",
help="Chemin vers le theme utilisé pour exporter le blog", help="Chemin vers le theme utilisé",
) )
parser.add_argument( parser.add_argument(
"-o",
"--output", "--output",
default="./output", default="./output",
help="Nom du dossier ou sera exporté le blog en html", help="Nom du dossier où sera exporté le blog en html",
) )
return vars(parser.parse_args(args)) return vars(parser.parse_args(args))
@@ -57,7 +64,6 @@ def main():
args = load_args() args = load_args()
if not Path(args["config"]).is_file(): if not Path(args["config"]).is_file():
# TODO gérer le cas où le fichier n'existe pas. Le créer ? Prendre les valeurs par défaut ?
print("Le fichier de configuration n'existe pas.") print("Le fichier de configuration n'existe pas.")
os._exit(1) os._exit(1)
@@ -67,22 +73,24 @@ def main():
match args["action"]: match args["action"]:
case "new": case "new":
page_title = " ".join(args["all"]) page_title = " ".join(args["all"])
new_page(page_title)
path = conf.inbox
if args["d"]:
path = conf.draft
subprocess.run(
["nvim", "+normal G$", "+startinsert", new_page(page_title, path)]
)
os._exit(0) os._exit(0)
case "make": case "make":
args = load_make_args(args["all"]) make_args = load_make_args(args["all"])
conf.overload(args) conf.overload(make_args)
blog = Blog(conf) blog = Blog(conf)
blog.make() blog.make(draft=args["d"])
os._exit(0) os._exit(0)
case "clean":
clean_output(conf.inbox)
os._exit(0)
case _: case _:
print("error") print("error")
os._exit(1) os._exit(1)

View File

@@ -5,8 +5,6 @@ description = "Add your description here"
readme = "README.md" readme = "README.md"
requires-python = ">=3.13" requires-python = ">=3.13"
dependencies = [ dependencies = [
"black>=25.1.0",
"flake8>=7.2.0",
"jinja2>=3.1.6", "jinja2>=3.1.6",
"markdown>=3.8", "markdown>=3.8",
"pygments>=2.19.1", "pygments>=2.19.1",

View File

@@ -1,75 +0,0 @@
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.codehilite .hll { background-color: #ffffcc }
.codehilite { background: #f8f8f8; }
.codehilite .c { color: #3D7B7B; font-style: italic } /* Comment */
.codehilite .err { border: 1px solid #F00 } /* Error */
.codehilite .k { color: #008000; font-weight: bold } /* Keyword */
.codehilite .o { color: #666 } /* Operator */
.codehilite .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */
.codehilite .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */
.codehilite .cp { color: #9C6500 } /* Comment.Preproc */
.codehilite .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */
.codehilite .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */
.codehilite .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */
.codehilite .gd { color: #A00000 } /* Generic.Deleted */
.codehilite .ge { font-style: italic } /* Generic.Emph */
.codehilite .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.codehilite .gr { color: #E40000 } /* Generic.Error */
.codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.codehilite .gi { color: #008400 } /* Generic.Inserted */
.codehilite .go { color: #717171 } /* Generic.Output */
.codehilite .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
.codehilite .gs { font-weight: bold } /* Generic.Strong */
.codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.codehilite .gt { color: #04D } /* Generic.Traceback */
.codehilite .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
.codehilite .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
.codehilite .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
.codehilite .kp { color: #008000 } /* Keyword.Pseudo */
.codehilite .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
.codehilite .kt { color: #B00040 } /* Keyword.Type */
.codehilite .m { color: #666 } /* Literal.Number */
.codehilite .s { color: #BA2121 } /* Literal.String */
.codehilite .na { color: #687822 } /* Name.Attribute */
.codehilite .nb { color: #008000 } /* Name.Builtin */
.codehilite .nc { color: #00F; font-weight: bold } /* Name.Class */
.codehilite .no { color: #800 } /* Name.Constant */
.codehilite .nd { color: #A2F } /* Name.Decorator */
.codehilite .ni { color: #717171; font-weight: bold } /* Name.Entity */
.codehilite .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */
.codehilite .nf { color: #00F } /* Name.Function */
.codehilite .nl { color: #767600 } /* Name.Label */
.codehilite .nn { color: #00F; font-weight: bold } /* Name.Namespace */
.codehilite .nt { color: #008000; font-weight: bold } /* Name.Tag */
.codehilite .nv { color: #19177C } /* Name.Variable */
.codehilite .ow { color: #A2F; font-weight: bold } /* Operator.Word */
.codehilite .w { color: #BBB } /* Text.Whitespace */
.codehilite .mb { color: #666 } /* Literal.Number.Bin */
.codehilite .mf { color: #666 } /* Literal.Number.Float */
.codehilite .mh { color: #666 } /* Literal.Number.Hex */
.codehilite .mi { color: #666 } /* Literal.Number.Integer */
.codehilite .mo { color: #666 } /* Literal.Number.Oct */
.codehilite .sa { color: #BA2121 } /* Literal.String.Affix */
.codehilite .sb { color: #BA2121 } /* Literal.String.Backtick */
.codehilite .sc { color: #BA2121 } /* Literal.String.Char */
.codehilite .dl { color: #BA2121 } /* Literal.String.Delimiter */
.codehilite .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
.codehilite .s2 { color: #BA2121 } /* Literal.String.Double */
.codehilite .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */
.codehilite .sh { color: #BA2121 } /* Literal.String.Heredoc */
.codehilite .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */
.codehilite .sx { color: #008000 } /* Literal.String.Other */
.codehilite .sr { color: #A45A77 } /* Literal.String.Regex */
.codehilite .s1 { color: #BA2121 } /* Literal.String.Single */
.codehilite .ss { color: #19177C } /* Literal.String.Symbol */
.codehilite .bp { color: #008000 } /* Name.Builtin.Pseudo */
.codehilite .fm { color: #00F } /* Name.Function.Magic */
.codehilite .vc { color: #19177C } /* Name.Variable.Class */
.codehilite .vg { color: #19177C } /* Name.Variable.Global */
.codehilite .vi { color: #19177C } /* Name.Variable.Instance */
.codehilite .vm { color: #19177C } /* Name.Variable.Magic */
.codehilite .il { color: #666 } /* Literal.Number.Integer.Long */

View File

@@ -1,22 +0,0 @@
body {
max-width: 720px;
margin: auto;
background: red;
color: white;
}
h1 {
}
h2 {
}
h3 {
}
a {
}

View File

@@ -1,28 +0,0 @@
<!DOCTYPE html>
<html lang="{{ lang|default("fr") }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="css/styles.css">
<title>{{ title }}</title>
</head>
<body>
<h1>{{ title }}</h1>
<p id="intro">{{ presentation }}</p>
{%- set ns = namespace(cur_month="") -%}
{%- for key, page in pages.items()|sort(attribute='1.date', reverse = true) -%}
{%- set month = page.date.strftime('%Y-%m') -%}
{%- if ns.cur_month != month -%}
{%- if ns.cur_month != "" %}
</ul>
{%- endif%}
<h2>{{ month }}</h2>
<ul>
{%- endif -%}
{%- set ns.cur_month = month %}
<li>{{ page.date.strftime('%d')}} : <a href="./pages/{{ key }}.html">{{ page.title }}</a>
{%- endfor %}
</ul>
</body>
</html>

View File

@@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="../css/styles.css">
<link rel="stylesheet" type="text/css" href="../css/codehilite.css">
<title>{{ title }}</title>
</head>
<body>
{{ content }}
</body>
</html>

122
uv.lock generated
View File

@@ -2,33 +2,11 @@ version = 1
revision = 2 revision = 2
requires-python = ">=3.13" requires-python = ">=3.13"
[[package]]
name = "black"
version = "25.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
{ name = "mypy-extensions" },
{ name = "packaging" },
{ name = "pathspec" },
{ name = "platformdirs" },
]
sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449, upload-time = "2025-01-29T04:15:40.373Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673, upload-time = "2025-01-29T05:37:20.574Z" },
{ url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190, upload-time = "2025-01-29T05:37:22.106Z" },
{ url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926, upload-time = "2025-01-29T04:18:58.564Z" },
{ url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613, upload-time = "2025-01-29T04:19:27.63Z" },
{ url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646, upload-time = "2025-01-29T04:15:38.082Z" },
]
[[package]] [[package]]
name = "blog" name = "blog"
version = "0.1.0" version = "0.1.0"
source = { virtual = "." } source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "black" },
{ name = "flake8" },
{ name = "jinja2" }, { name = "jinja2" },
{ name = "markdown" }, { name = "markdown" },
{ name = "pygments" }, { name = "pygments" },
@@ -37,49 +15,12 @@ dependencies = [
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [
{ name = "black", specifier = ">=25.1.0" },
{ name = "flake8", specifier = ">=7.2.0" },
{ name = "jinja2", specifier = ">=3.1.6" }, { name = "jinja2", specifier = ">=3.1.6" },
{ name = "markdown", specifier = ">=3.8" }, { name = "markdown", specifier = ">=3.8" },
{ name = "pygments", specifier = ">=2.19.1" }, { name = "pygments", specifier = ">=2.19.1" },
{ name = "pyyaml", specifier = ">=6.0.2" }, { name = "pyyaml", specifier = ">=6.0.2" },
] ]
[[package]]
name = "click"
version = "8.2.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
[[package]]
name = "flake8"
version = "7.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mccabe" },
{ name = "pycodestyle" },
{ name = "pyflakes" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e7/c4/5842fc9fc94584c455543540af62fd9900faade32511fab650e9891ec225/flake8-7.2.0.tar.gz", hash = "sha256:fa558ae3f6f7dbf2b4f22663e5343b6b6023620461f8d4ff2019ef4b5ee70426", size = 48177, upload-time = "2025-03-29T20:08:39.329Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/83/5c/0627be4c9976d56b1217cb5187b7504e7fd7d3503f8bfd312a04077bd4f7/flake8-7.2.0-py2.py3-none-any.whl", hash = "sha256:93b92ba5bdb60754a6da14fa3b93a9361fd00a59632ada61fd7b130436c40343", size = 57786, upload-time = "2025-03-29T20:08:37.902Z" },
]
[[package]] [[package]]
name = "jinja2" name = "jinja2"
version = "3.1.6" version = "3.1.6"
@@ -129,69 +70,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" },
] ]
[[package]]
name = "mccabe"
version = "0.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658, upload-time = "2022-01-24T01:14:51.113Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" },
]
[[package]]
name = "mypy-extensions"
version = "1.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
]
[[package]]
name = "packaging"
version = "25.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
]
[[package]]
name = "pathspec"
version = "0.12.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" },
]
[[package]]
name = "platformdirs"
version = "4.3.8"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" },
]
[[package]]
name = "pycodestyle"
version = "2.13.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/04/6e/1f4a62078e4d95d82367f24e685aef3a672abfd27d1a868068fed4ed2254/pycodestyle-2.13.0.tar.gz", hash = "sha256:c8415bf09abe81d9c7f872502a6eee881fbe85d8763dd5b9924bb0a01d67efae", size = 39312, upload-time = "2025-03-29T17:33:30.669Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/07/be/b00116df1bfb3e0bb5b45e29d604799f7b91dd861637e4d448b4e09e6a3e/pycodestyle-2.13.0-py2.py3-none-any.whl", hash = "sha256:35863c5974a271c7a726ed228a14a4f6daf49df369d8c50cd9a6f58a5e143ba9", size = 31424, upload-time = "2025-03-29T17:33:29.405Z" },
]
[[package]]
name = "pyflakes"
version = "3.3.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/af/cc/1df338bd7ed1fa7c317081dcf29bf2f01266603b301e6858856d346a12b3/pyflakes-3.3.2.tar.gz", hash = "sha256:6dfd61d87b97fba5dcfaaf781171ac16be16453be6d816147989e7f6e6a9576b", size = 64175, upload-time = "2025-03-31T13:21:20.34Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/15/40/b293a4fa769f3b02ab9e387c707c4cbdc34f073f945de0386107d4e669e6/pyflakes-3.3.2-py2.py3-none-any.whl", hash = "sha256:5039c8339cbb1944045f4ee5466908906180f13cc99cc9949348d10f82a5c32a", size = 63164, upload-time = "2025-03-31T13:21:18.503Z" },
]
[[package]] [[package]]
name = "pygments" name = "pygments"
version = "2.19.1" version = "2.19.1"