git_ew
¤
git-ew package.
Git Email Workflow
Classes:
-
Base–Base class for all models.
-
CommentCreate–Model for creating a comment.
-
Configuration–Stores application configuration.
-
Database–Database manager.
-
EmailFetcher–Base class for email fetchers.
-
EmailSender–Handle sending emails.
-
EmailSource–Represents an email archive source to fetch from.
-
IMAPFetcher–Fetch emails from IMAP folders, including Fastmail accounts.
-
LinkExtractor–Extract .tgz archive links and dates from HTML.
-
MaildirFetcher–Fetch emails from a Maildir directory.
-
MboxFetcher–Fetch emails from an mbox file.
-
Message–Represents an individual email message in a thread.
-
ParsedEmail–Represents a parsed email message.
-
PublicInboxFetcher–Fetch emails from a public-inbox archive URL.
-
Thread–Represents an email thread (like a GitHub issue or PR).
-
ThreadNode–Represents a node in a thread tree.
-
ThreadUpdate–Model for updating a thread.
Functions:
-
append_sent_message–Append a sent message to an IMAP folder using a resolved password.
-
build_thread_tree–Build a tree structure from a flat list of messages.
-
config_command–Configure email delivery and ingestion sources interactively.
-
create_app–Create and configure the FastAPI application.
-
create_email_sender–Create an email sender with a resolved password.
-
decode_email_header–Decode RFC 2047 encoded-word headers to plain text.
-
download_archive–Download a single archive.
-
extract_body_and_patch–Extract the plain-text body and any MIME patch attachment.
-
extract_emails_from_archive–Extract email files from a .tgz archive.
-
extract_quoted_text–Separate new content from quoted text.
-
fetch_archive_list–Fetch the list of available archives from zsh.org with dates.
-
find_email_by_xseq–Find an email by its X-seq number by searching through archives.
-
get_email_in_reply_to–Extract In-Reply-To from email header.
-
get_email_message_id–Extract message ID from email header.
-
get_email_references–Extract References from email header.
-
get_email_xseq–Extract X-seq number from email header.
-
get_engine–Create database engine.
-
get_fetcher–Get an email fetcher based on source type.
-
get_missing_archives–Determine which matching archives need to be downloaded.
-
get_parser–Return the CLI argument parser.
-
get_session_maker–Get a session maker.
-
get_thread_api–Get thread details.
-
index–Show all email threads.
-
ingest_archive–Ingest emails from an archive into the database.
-
ingest_archives–Ingest all zsh-workers archives in a directory.
-
init_db–Initialize the database.
-
lifespan–Application lifespan manager.
-
list_threads–Get list of threads.
-
main–Run the main program.
-
normalize_message_id–Normalize a message ID by removing angle brackets.
-
parse_email–Parse a raw email message.
-
parse_email_address–Parse From header to extract name and email address.
-
post_comment–Post a comment to a thread (sends an email).
-
render_markdown–Render plain text as markdown HTML.
-
resolve_password–Resolve a configured password.
-
sync_all_sources–Sync emails from all configured sources.
-
sync_command–Run email sync as a standalone command.
-
sync_emails–Sync emails from configured sources.
-
thread_to_nested_structure–Convert thread tree to nested structure, with single-children popped out to sibling level.
-
update_thread–Update a thread.
-
view_thread–View a specific thread with all messages.
Attributes:
-
BASE_URL–Base URL for zsh-workers mailing list archives.
-
app–FastAPI application instance.
-
db(Database | None) –Global database instance.
-
static_dir–Path to static files directory.
-
template_dir–Path to templates directory.
-
templates–Jinja2 templates renderer.
BASE_URL
module-attribute
¤
BASE_URL = 'https://www.zsh.org/mla/zsh-workers/'
Base URL for zsh-workers mailing list archives.
app
module-attribute
¤
app = FastAPI(
title="git-ew",
description="Git Email Workflow",
lifespan=lifespan,
)
FastAPI application instance.
static_dir
module-attribute
¤
static_dir = Path(__file__).parent.parent / 'static'
Path to static files directory.
templates
module-attribute
¤
templates = Jinja2Templates(directory=str(template_dir))
Jinja2 templates renderer.
Base
¤
Bases: DeclarativeBase
Base class for all models.
CommentCreate
¤
Bases: BaseModel
Model for creating a comment.
Attributes:
-
body(str) –Comment body text.
-
in_reply_to(str | None) –Message ID this comment is replying to.
Configuration
¤
Bases: Base
Stores application configuration.
Attributes:
-
__tablename__–Database table name.
-
id(Mapped[int]) –Configuration unique identifier.
-
key(Mapped[str]) –Configuration key.
-
value(Mapped[str]) –Configuration value.
__tablename__
class-attribute
instance-attribute
¤
__tablename__ = 'configuration'
Database table name.
id
class-attribute
instance-attribute
¤
id: Mapped[int] = mapped_column(Integer, primary_key=True)
Configuration unique identifier.
Database
¤
Database(
database_url: str = "sqlite+aiosqlite:///./git_ew.db",
)
Database manager.
Parameters:
Methods:
-
create_message–Create a new message.
-
create_thread–Create a new thread.
-
get_config–Get a configuration value.
-
get_email_sources–Get all email sources.
-
get_message_by_id–Get a message by its message ID.
-
get_thread–Get a thread by ID with all messages.
-
get_thread_by_message_id–Get a thread by first message ID.
-
get_threads–Get threads with optional filtering.
-
init_db–Initialize database tables.
-
session–Get a database session.
-
set_config–Set a configuration value.
-
update_message_patch–Add patch content to an existing message.
-
update_thread_status–Update thread status.
Attributes:
-
engine–SQLAlchemy async engine instance.
-
session_maker–Async session factory.
Source code in src/git_ew/_internal/database.py
41 42 43 44 45 46 47 48 49 50 | |
engine
instance-attribute
¤
engine = create_async_engine(database_url, echo=False)
SQLAlchemy async engine instance.
session_maker
instance-attribute
¤
session_maker = async_sessionmaker(
self.engine, class_=AsyncSession, expire_on_commit=False
)
Async session factory.
create_message
async
¤
create_message(
message_id: str,
thread_id: int,
from_email: str,
from_name: str,
subject: str,
date: datetime,
body: str,
*,
in_reply_to: str | None = None,
is_patch: bool = False,
patch_content: str | None = None,
raw_email: str | None = None,
) -> Message
Create a new message.
Parameters:
-
(message_id¤str) –Unique message ID.
-
(thread_id¤int) –Thread ID this message belongs to.
-
(from_email¤str) –Sender email.
-
(from_name¤str) –Sender name.
-
(subject¤str) –Message subject.
-
(date¤datetime) –Message date.
-
(body¤str) –Message body.
-
(in_reply_to¤str | None, default:None) –ID of message this replies to.
-
(is_patch¤bool, default:False) –Whether this message contains a patch.
-
(patch_content¤str | None, default:None) –Patch content if applicable.
-
(raw_email¤str | None, default:None) –Raw email content.
Returns:
-
Message–Created message.
Source code in src/git_ew/_internal/database.py
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 200 201 202 203 204 205 206 207 208 209 210 | |
create_thread
async
¤
Create a new thread.
Parameters:
-
(subject¤str) –Thread subject.
-
(first_message_id¤str) –ID of the first message.
-
(is_patch¤bool, default:False) –Whether this thread contains patches.
Returns:
-
Thread–Created thread.
Source code in src/git_ew/_internal/database.py
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | |
get_config
async
¤
Get a configuration value.
Parameters:
Returns:
-
Any–Configuration value or default.
Source code in src/git_ew/_internal/database.py
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | |
get_email_sources
async
¤
get_email_sources() -> list[EmailSource]
Get all email sources.
Returns:
-
list[EmailSource]–List of email sources.
Source code in src/git_ew/_internal/database.py
275 276 277 278 279 280 281 282 283 284 | |
get_message_by_id
async
¤
get_message_by_id(message_id: str) -> Message | None
Get a message by its message ID.
Parameters:
Returns:
-
Message | None–Message or None if not found.
Source code in src/git_ew/_internal/database.py
212 213 214 215 216 217 218 219 220 221 222 223 224 | |
get_thread
async
¤
Get a thread by ID with all messages.
Parameters:
Returns:
-
Thread | None–Thread or None if not found.
Source code in src/git_ew/_internal/database.py
94 95 96 97 98 99 100 101 102 103 104 105 106 | |
get_thread_by_message_id
async
¤
get_thread_by_message_id(message_id: str) -> Thread | None
Get a thread by first message ID.
Parameters:
Returns:
-
Thread | None–Thread or None if not found.
Source code in src/git_ew/_internal/database.py
108 109 110 111 112 113 114 115 116 117 118 119 120 | |
get_threads
async
¤
get_threads(
status: str | None = None,
limit: int | None = None,
offset: int = 0,
) -> list[Thread]
Get threads with optional filtering.
Parameters:
-
(status¤str | None, default:None) –Filter by status (open/closed).
-
(limit¤int | None, default:None) –Maximum number of threads to return.
-
(offset¤int, default:0) –Number of threads to skip.
Returns:
Source code in src/git_ew/_internal/database.py
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 | |
init_db
async
¤
init_db() -> None
Initialize database tables.
Source code in src/git_ew/_internal/database.py
52 53 54 55 | |
session
async
¤
session() -> AsyncIterator[AsyncSession]
Get a database session.
Source code in src/git_ew/_internal/database.py
57 58 59 60 61 62 63 64 65 66 | |
set_config
async
¤
Set a configuration value.
Parameters:
Source code in src/git_ew/_internal/database.py
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | |
update_message_patch
async
¤
Add patch content to an existing message.
Source code in src/git_ew/_internal/database.py
226 227 228 229 230 231 232 233 234 235 236 237 | |
update_thread_status
async
¤
Update thread status.
Parameters:
Source code in src/git_ew/_internal/database.py
286 287 288 289 290 291 292 293 294 295 296 297 | |
EmailFetcher
¤
Base class for email fetchers.
Methods:
-
fetch_emails–Fetch emails from the source.
fetch_emails
¤
fetch_emails(
since: str | None = None,
) -> AsyncIterator[ParsedEmail]
Fetch emails from the source.
Parameters:
Yields:
-
AsyncIterator[ParsedEmail]–ParsedEmail objects.
Source code in src/git_ew/_internal/email_fetcher.py
44 45 46 47 48 49 50 51 52 53 | |
EmailSender
¤
EmailSender(
smtp_host: str,
smtp_port: int,
from_email: str,
from_name: str,
*,
username: str | None = None,
password: str | None = None,
use_tls: bool = True,
)
Handle sending emails.
Parameters:
-
(smtp_host¤str) –SMTP server hostname.
-
(smtp_port¤int) –SMTP server port.
-
(from_email¤str) –Sender email address.
-
(from_name¤str) –Sender name.
-
(username¤str | None, default:None) –SMTP username (if required).
-
(password¤str | None, default:None) –SMTP password (if required).
-
(use_tls¤bool, default:True) –Whether to use TLS.
Methods:
-
build_email–Build an email without sending it.
-
build_reply–Build a reply email without sending it.
-
send_email–Send a plain text email.
-
send_message–Send a prepared email over SMTP.
-
send_reply–Send a reply email.
Attributes:
-
from_email–Sender email address.
-
from_name–Sender name.
-
password–SMTP password.
-
smtp_host–SMTP server hostname.
-
smtp_port–SMTP server port.
-
use_tls–Whether to use TLS.
-
username–SMTP username.
Source code in src/git_ew/_internal/email_sender.py
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | |
build_email
¤
build_email(
to_email: str,
subject: str,
body: str,
in_reply_to: str | None = None,
references: list[str] | None = None,
cc: list[str] | None = None,
) -> EmailMessage
Build an email without sending it.
Source code in src/git_ew/_internal/email_sender.py
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 | |
build_reply
¤
build_reply(
to_email: str,
subject: str,
body: str,
in_reply_to: str,
references: list[str] | None = None,
cc: list[str] | None = None,
) -> EmailMessage
Build a reply email without sending it.
Source code in src/git_ew/_internal/email_sender.py
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 | |
send_email
¤
send_email(
to_email: str,
subject: str,
body: str,
in_reply_to: str | None = None,
references: list[str] | None = None,
cc: list[str] | None = None,
) -> str
Send a plain text email.
Parameters:
-
(to_email¤str) –Recipient email address.
-
(subject¤str) –Email subject.
-
(body¤str) –Email body (plain text).
-
(in_reply_to¤str | None, default:None) –Message ID this replies to.
-
(references¤list[str] | None, default:None) –List of message IDs in the thread.
-
(cc¤list[str] | None, default:None) –List of CC recipients.
Returns:
-
str–Message ID of sent email.
Source code in src/git_ew/_internal/email_sender.py
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 | |
send_message
¤
send_message(msg: EmailMessage) -> None
Send a prepared email over SMTP.
Source code in src/git_ew/_internal/email_sender.py
130 131 132 133 134 135 136 137 138 139 | |
send_reply
¤
send_reply(
to_email: str,
subject: str,
body: str,
in_reply_to: str,
references: list[str] | None = None,
cc: list[str] | None = None,
) -> str
Send a reply email.
Parameters:
-
(to_email¤str) –Recipient email address.
-
(subject¤str) –Email subject (will be prefixed with "Re:" if not present).
-
(body¤str) –Email body (plain text).
-
(in_reply_to¤str) –Message ID this replies to.
-
(references¤list[str] | None, default:None) –List of message IDs in the thread.
-
(cc¤list[str] | None, default:None) –List of CC recipients.
Returns:
-
str–Message ID of sent email.
Source code in src/git_ew/_internal/email_sender.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 | |
EmailSource
¤
Bases: Base
Represents an email archive source to fetch from.
Attributes:
-
__tablename__–Database table name.
-
config(Mapped[str]) –JSON configuration for the source.
-
enabled(Mapped[bool]) –Whether this source is enabled.
-
id(Mapped[int]) –Source unique identifier.
-
last_synced(Mapped[datetime | None]) –When this source was last synced.
-
name(Mapped[str]) –Source name.
-
source_type(Mapped[str]) –Type of email source (maildir, mbox, imap, etc).
__tablename__
class-attribute
instance-attribute
¤
__tablename__ = 'email_sources'
Database table name.
config
class-attribute
instance-attribute
¤
config: Mapped[str] = mapped_column(Text, nullable=False)
JSON configuration for the source.
enabled
class-attribute
instance-attribute
¤
enabled: Mapped[bool] = mapped_column(
Boolean, default=True, nullable=False
)
Whether this source is enabled.
id
class-attribute
instance-attribute
¤
id: Mapped[int] = mapped_column(Integer, primary_key=True)
Source unique identifier.
last_synced
class-attribute
instance-attribute
¤
last_synced: Mapped[datetime | None] = mapped_column(
DateTime, nullable=True
)
When this source was last synced.
IMAPFetcher
¤
IMAPFetcher(
host: str,
username: str,
password: str,
*,
port: int = 993,
folders: list[str] | None = None,
mailing_list: dict[str, list[str]] | None = None,
)
Bases: EmailFetcher
Fetch emails from IMAP folders, including Fastmail accounts.
Methods:
-
fetch_emails–Fetch and parse messages from configured IMAP folders.
Attributes:
-
folders–Folders to fetch messages from.
-
host–IMAP server hostname.
-
mailing_list–Mailing list headers used to filter messages.
-
password–IMAP account password.
-
port–IMAP server port.
-
username–IMAP account username.
Source code in src/git_ew/_internal/email_fetcher.py
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | |
mailing_list
instance-attribute
¤
mailing_list = mailing_list or {}
Mailing list headers used to filter messages.
fetch_emails
async
¤
fetch_emails(
since: str | None = None,
) -> AsyncIterator[ParsedEmail]
Fetch and parse messages from configured IMAP folders.
Source code in src/git_ew/_internal/email_fetcher.py
168 169 170 171 172 173 174 175 176 | |
LinkExtractor
¤
LinkExtractor()
Bases: HTMLParser
Extract .tgz archive links and dates from HTML.
Methods:
-
handle_data–Extract date information from pre-formatted text.
-
handle_endtag–Track end of pre tag.
-
handle_starttag–Track pre tags and extract href attributes.
Attributes:
Source code in src/git_ew/_internal/mailing_lists/zsh_workers/sync_archives.py
37 38 39 40 41 42 | |
archives
instance-attribute
¤
Archive filenames and their dates found in the page.
handle_data
¤
handle_data(data: str) -> None
Extract date information from pre-formatted text.
Source code in src/git_ew/_internal/mailing_lists/zsh_workers/sync_archives.py
60 61 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 | |
handle_endtag
¤
handle_endtag(tag: str) -> None
Track end of pre tag.
Source code in src/git_ew/_internal/mailing_lists/zsh_workers/sync_archives.py
55 56 57 58 | |
handle_starttag
¤
Track pre tags and extract href attributes.
Source code in src/git_ew/_internal/mailing_lists/zsh_workers/sync_archives.py
44 45 46 47 48 49 50 51 52 53 | |
MaildirFetcher
¤
MaildirFetcher(maildir_path: str)
Bases: EmailFetcher
Fetch emails from a Maildir directory.
Parameters:
Methods:
-
fetch_emails–Fetch emails from Maildir.
Attributes:
-
maildir_path–Path to the Maildir directory.
Source code in src/git_ew/_internal/email_fetcher.py
59 60 61 62 63 64 65 66 | |
fetch_emails
async
¤
fetch_emails(
since: str | None = None,
) -> AsyncIterator[ParsedEmail]
Fetch emails from Maildir.
Parameters:
Yields:
-
AsyncIterator[ParsedEmail]–ParsedEmail objects.
Source code in src/git_ew/_internal/email_fetcher.py
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 | |
MboxFetcher
¤
Bases: EmailFetcher
Fetch emails from an mbox file.
Parameters:
Methods:
-
fetch_emails–Fetch emails from mbox.
Attributes:
-
mbox_path–Path to the mbox file.
Source code in src/git_ew/_internal/email_fetcher.py
102 103 104 105 106 107 108 109 | |
fetch_emails
async
¤
fetch_emails(
since: str | None = None,
) -> AsyncIterator[ParsedEmail]
Fetch emails from mbox.
Parameters:
Yields:
-
AsyncIterator[ParsedEmail]–ParsedEmail objects.
Source code in src/git_ew/_internal/email_fetcher.py
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | |
Message
¤
Bases: Base
Represents an individual email message in a thread.
Attributes:
-
__tablename__–Database table name.
-
body(Mapped[str]) –Message body text.
-
date(Mapped[datetime]) –Email date.
-
from_email(Mapped[str]) –Sender email address.
-
from_name(Mapped[str]) –Sender name.
-
id(Mapped[int]) –Message unique identifier.
-
in_reply_to(Mapped[str | None]) –Message ID this reply refers to.
-
is_patch(Mapped[bool]) –Whether this message contains a patch.
-
message_id(Mapped[str]) –Email message ID header.
-
patch_content(Mapped[str | None]) –Extracted patch content.
-
raw_email(Mapped[str]) –Raw email content.
-
subject(Mapped[str]) –Email subject.
-
thread(Mapped[Thread]) –Thread this message belongs to.
-
thread_id(Mapped[int]) –Thread this message belongs to.
body
class-attribute
instance-attribute
¤
body: Mapped[str] = mapped_column(Text, nullable=False)
Message body text.
date
class-attribute
instance-attribute
¤
date: Mapped[datetime] = mapped_column(
DateTime, nullable=False, index=True
)
Email date.
from_email
class-attribute
instance-attribute
¤
from_email: Mapped[str] = mapped_column(
String(255), nullable=False
)
Sender email address.
from_name
class-attribute
instance-attribute
¤
from_name: Mapped[str] = mapped_column(
String(255), nullable=False
)
Sender name.
id
class-attribute
instance-attribute
¤
id: Mapped[int] = mapped_column(Integer, primary_key=True)
Message unique identifier.
in_reply_to
class-attribute
instance-attribute
¤
in_reply_to: Mapped[str | None] = mapped_column(
String(500), nullable=True, index=True
)
Message ID this reply refers to.
is_patch
class-attribute
instance-attribute
¤
is_patch: Mapped[bool] = mapped_column(
Boolean, default=False, nullable=False
)
Whether this message contains a patch.
message_id
class-attribute
instance-attribute
¤
message_id: Mapped[str] = mapped_column(
String(500), unique=True, nullable=False, index=True
)
Email message ID header.
patch_content
class-attribute
instance-attribute
¤
patch_content: Mapped[str | None] = mapped_column(
Text, nullable=True
)
Extracted patch content.
raw_email
class-attribute
instance-attribute
¤
raw_email: Mapped[str] = mapped_column(Text, nullable=True)
Raw email content.
subject
class-attribute
instance-attribute
¤
subject: Mapped[str] = mapped_column(
String(500), nullable=False
)
Email subject.
ParsedEmail
¤
ParsedEmail(
message_id: str,
from_email: str,
from_name: str,
subject: str,
date: datetime,
body: str,
*,
in_reply_to: str | None = None,
references: list[str] | None = None,
is_patch: bool = False,
patch_content: str | None = None,
raw: str | None = None,
)
Represents a parsed email message.
Parameters:
-
(message_id¤str) –Unique message ID.
-
(from_email¤str) –Sender email address.
-
(from_name¤str) –Sender name.
-
(subject¤str) –Email subject.
-
(date¤datetime) –Email date.
-
(body¤str) –Email body text.
-
(in_reply_to¤str | None, default:None) –Message ID this replies to.
-
(references¤list[str] | None, default:None) –List of referenced message IDs.
-
(is_patch¤bool, default:False) –Whether this email contains a patch.
-
(patch_content¤str | None, default:None) –Extracted patch content.
-
(raw¤str | None, default:None) –Raw email content.
Methods:
-
get_thread_id–Get the thread ID (first message in chain).
Attributes:
-
body–Email body text.
-
clean_subject(str) –Get subject with RE: and similar prefixes removed.
-
date–Email date.
-
from_email–Sender email address.
-
from_name–Sender name.
-
in_reply_to–Message ID this replies to.
-
is_patch–Whether this email contains a patch.
-
message_id–Unique message ID.
-
patch_content–Extracted patch content.
-
raw–Raw email content.
-
references–List of referenced message IDs.
-
subject–Email subject.
Source code in src/git_ew/_internal/email_parser.py
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 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 | |
get_thread_id
¤
get_thread_id() -> str
Get the thread ID (first message in chain).
Returns:
-
str–Thread ID (the first message ID in the references chain).
Source code in src/git_ew/_internal/email_parser.py
90 91 92 93 94 95 96 97 98 99 100 | |
PublicInboxFetcher
¤
PublicInboxFetcher(archive_url: str)
Bases: EmailFetcher
Fetch emails from a public-inbox archive URL.
Parameters:
Methods:
-
fetch_emails–Fetch emails from public-inbox archive.
Attributes:
-
archive_url–Base URL of the public-inbox archive.
Source code in src/git_ew/_internal/email_fetcher.py
249 250 251 252 253 254 255 256 | |
archive_url
instance-attribute
¤
archive_url = archive_url.rstrip('/')
Base URL of the public-inbox archive.
fetch_emails
¤
fetch_emails(
since: str | None = None,
) -> AsyncIterator[ParsedEmail]
Fetch emails from public-inbox archive.
Parameters:
Yields:
-
AsyncIterator[ParsedEmail]–ParsedEmail objects.
Source code in src/git_ew/_internal/email_fetcher.py
258 259 260 261 262 263 264 265 266 267 268 269 270 | |
Thread
¤
Bases: Base
Represents an email thread (like a GitHub issue or PR).
Attributes:
-
__tablename__–Database table name.
-
created_at(Mapped[datetime]) –When the thread was created.
-
first_message_id(Mapped[str]) –ID of the first message in the thread.
-
id(Mapped[int]) –Thread unique identifier.
-
is_patch(Mapped[bool]) –Whether this thread contains patches.
-
messages(Mapped[list[Message]]) –Messages in this thread.
-
status(Mapped[str]) –Thread status (open or closed).
-
subject(Mapped[str]) –Thread subject line.
-
updated_at(Mapped[datetime]) –When the thread was last updated.
created_at
class-attribute
instance-attribute
¤
created_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False
)
When the thread was created.
first_message_id
class-attribute
instance-attribute
¤
first_message_id: Mapped[str] = mapped_column(
String(500), unique=True, nullable=False
)
ID of the first message in the thread.
id
class-attribute
instance-attribute
¤
id: Mapped[int] = mapped_column(Integer, primary_key=True)
Thread unique identifier.
is_patch
class-attribute
instance-attribute
¤
is_patch: Mapped[bool] = mapped_column(
Boolean, default=False, nullable=False
)
Whether this thread contains patches.
messages
class-attribute
instance-attribute
¤
messages: Mapped[list[Message]] = relationship(
"Message",
back_populates="thread",
cascade="all, delete-orphan",
)
Messages in this thread.
status
class-attribute
instance-attribute
¤
status: Mapped[str] = mapped_column(
String(50), default="open", nullable=False
)
Thread status (open or closed).
ThreadNode
dataclass
¤
ThreadNode(message: Message, children: list[ThreadNode])
Represents a node in a thread tree.
Attributes:
-
children(list[ThreadNode]) –Child nodes in the thread tree.
-
message(Message) –The message at this node.
ThreadUpdate
¤
append_sent_message
¤
append_sent_message(
msg: EmailMessage,
config: dict[str, Any],
folder: str = "Sent",
) -> None
Append a sent message to an IMAP folder using a resolved password.
Source code in src/git_ew/_internal/email_sender.py
194 195 196 197 198 199 200 201 202 203 204 205 206 207 | |
build_thread_tree
¤
build_thread_tree(
messages: list[Message],
) -> list[ThreadNode]
Build a tree structure from a flat list of messages.
Parameters:
Returns:
-
list[ThreadNode]–List of root ThreadNodes.
Source code in src/git_ew/_internal/thread_utils.py
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | |
config_command
async
¤
config_command() -> None
Configure email delivery and ingestion sources interactively.
Source code in src/git_ew/_internal/config.py
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | |
create_app
¤
create_app() -> FastAPI
Create and configure the FastAPI application.
Returns:
-
FastAPI–FastAPI application instance.
Source code in src/git_ew/_internal/app.py
412 413 414 415 416 417 418 | |
create_email_sender
¤
create_email_sender(config: dict[str, Any]) -> EmailSender
Create an email sender with a resolved password.
Source code in src/git_ew/_internal/email_sender.py
210 211 212 213 214 215 216 217 218 219 220 | |
decode_email_header
¤
decode_email_header(header_value: str) -> str
Decode RFC 2047 encoded-word headers to plain text.
Parameters:
Returns:
-
str–Decoded string.
Source code in src/git_ew/_internal/mailing_lists/zsh_workers/ingest.py
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | |
download_archive
¤
download_archive(filename: str, archive_dir: Path) -> bool
Download a single archive.
Parameters:
-
(filename¤str) –Archive filename to download.
-
(archive_dir¤Path) –Directory to save the archive to.
Returns:
-
bool–True if download succeeded, False otherwise.
Source code in src/git_ew/_internal/mailing_lists/zsh_workers/sync_archives.py
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | |
extract_body_and_patch
¤
Extract the plain-text body and any MIME patch attachment.
Source code in src/git_ew/_internal/email_parser.py
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | |
extract_emails_from_archive
¤
Extract email files from a .tgz archive.
Parameters:
Yields:
Source code in src/git_ew/_internal/mailing_lists/zsh_workers/ingest.py
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | |
extract_quoted_text
¤
Separate new content from quoted text.
Parameters:
Returns:
Source code in src/git_ew/_internal/email_parser.py
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 | |
fetch_archive_list
¤
Fetch the list of available archives from zsh.org with dates.
Returns:
-
dict[str, date | None]–Dict mapping archive filenames to their dates (or None if date couldn't be parsed)
Raises:
-
URLError–If the page cannot be fetched.
Source code in src/git_ew/_internal/mailing_lists/zsh_workers/sync_archives.py
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | |
find_email_by_xseq
¤
find_email_by_xseq(
archive_dir: Path, xseq_number: int
) -> tuple[Message, int] | None
Find an email by its X-seq number by searching through archives.
Parameters:
-
(archive_dir¤Path) –Directory containing .tgz archives.
-
(xseq_number¤int) –The X-seq number to search for (e.g., 44316 for msg00353).
Returns:
Source code in src/git_ew/_internal/mailing_lists/zsh_workers/ingest.py
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | |
get_email_in_reply_to
¤
Extract In-Reply-To from email header.
Parameters:
Returns:
-
str | None–In-Reply-To ID or None if not found.
Source code in src/git_ew/_internal/mailing_lists/zsh_workers/ingest.py
148 149 150 151 152 153 154 155 156 157 158 159 160 | |
get_email_message_id
¤
Extract message ID from email header.
Parameters:
Returns:
-
str | None–Message ID or None if not found.
Source code in src/git_ew/_internal/mailing_lists/zsh_workers/ingest.py
132 133 134 135 136 137 138 139 140 141 142 143 144 145 | |
get_email_references
¤
Extract References from email header.
Parameters:
Returns:
Source code in src/git_ew/_internal/mailing_lists/zsh_workers/ingest.py
163 164 165 166 167 168 169 170 171 172 173 174 175 176 | |
get_email_xseq
¤
Extract X-seq number from email header.
The X-seq header contains the sequence number in the mailing list format: "zsh-workers 44316" means message #44316.
Parameters:
Returns:
-
int | None–Sequence number or None if not found.
Source code in src/git_ew/_internal/mailing_lists/zsh_workers/ingest.py
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | |
get_engine
¤
get_engine(
database_url: str = "sqlite+aiosqlite:///./git_ew.db",
) -> Engine
Create database engine.
Source code in src/git_ew/_internal/models.py
135 136 137 | |
get_fetcher
¤
get_fetcher(
source_type: str, config: dict[str, Any]
) -> EmailFetcher
Get an email fetcher based on source type.
Parameters:
-
(source_type¤str) –Type of source (maildir, mbox, imap, public-inbox).
-
(config¤dict[str, Any]) –Configuration for the fetcher.
Returns:
-
EmailFetcher–EmailFetcher instance.
Raises:
-
ValueError–If source type is unknown.
Source code in src/git_ew/_internal/email_fetcher.py
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 | |
get_missing_archives
¤
get_missing_archives(
archive_dir: Path,
available_archives: dict[str, date | None],
since: date | None = None,
until: date | None = None,
) -> list[str]
Determine which matching archives need to be downloaded.
Parameters:
-
(archive_dir¤Path) –Directory where archives are stored.
-
(available_archives¤dict[str, date | None]) –Dict of available archive filenames to dates.
-
(since¤date | None, default:None) –Only include archives from this date onwards.
-
(until¤date | None, default:None) –Only include archives up to this date.
Returns:
Source code in src/git_ew/_internal/mailing_lists/zsh_workers/sync_archives.py
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | |
get_parser
¤
get_parser() -> ArgumentParser
Return the CLI argument parser.
Returns:
-
ArgumentParser–An argparse parser.
Source code in src/git_ew/_internal/cli.py
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 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | |
get_session_maker
¤
get_session_maker(engine: Engine) -> sessionmaker
Get a session maker.
Source code in src/git_ew/_internal/models.py
147 148 149 | |
get_thread_api
async
¤
Get thread details.
Parameters:
Returns:
-
JSONResponse–JSON response with thread and messages.
Source code in src/git_ew/_internal/app.py
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 | |
index
async
¤
index(request: Request) -> HTMLResponse
Show all email threads.
Source code in src/git_ew/_internal/app.py
149 150 151 152 153 154 155 156 157 158 159 160 | |
ingest_archive
¤
ingest_archive(
archive_path: Path,
db: _SyncDatabaseProtocol,
on_email_found: Callable[[str, Message, str], object]
| None = None,
) -> tuple[int, int]
Ingest emails from an archive into the database.
Parameters:
-
(archive_path¤Path) –Path to the .tgz archive.
-
(db¤_SyncDatabaseProtocol) –Database instance.
-
(on_email_found¤Callable[[str, Message, str], object] | None, default:None) –Optional callback function called when an email is found. Called with (filename, msg, message_id).
Returns:
Source code in src/git_ew/_internal/mailing_lists/zsh_workers/ingest.py
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 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 | |
ingest_archives
¤
ingest_archives(
archive_dir: Path = Path(".archives"),
database_url: str = "sqlite:///./git_ew.db",
*,
filenames: Iterable[str] | None = None,
verbose: bool = True,
) -> tuple[int, int]
Ingest all zsh-workers archives in a directory.
Parameters:
-
(archive_dir¤Path, default:Path('.archives')) –Directory containing
.tgzarchives. -
(database_url¤str, default:'sqlite:///./git_ew.db') –SQLAlchemy URL for the target database.
-
(filenames¤Iterable[str] | None, default:None) –Archive filenames to ingest. Ingest all local archives if omitted.
-
(verbose¤bool, default:True) –Whether to log per-archive progress.
Returns:
Source code in src/git_ew/_internal/mailing_lists/zsh_workers/ingest.py
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 | |
init_db
¤
init_db(
database_url: str = "sqlite:///./git_ew.db",
) -> Engine
Initialize the database.
Source code in src/git_ew/_internal/models.py
140 141 142 143 144 | |
lifespan
async
¤
lifespan(app: FastAPI) -> AsyncIterator[None]
Application lifespan manager.
Source code in src/git_ew/_internal/app.py
64 65 66 67 68 69 70 | |
list_threads
async
¤
Get list of threads.
Parameters:
-
(status¤str | None, default:None) –Filter by status.
-
(limit¤int, default:50) –Maximum number of threads.
-
(offset¤int, default:0) –Number of threads to skip.
Returns:
-
JSONResponse–JSON response with threads.
Source code in src/git_ew/_internal/app.py
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 | |
main
¤
Run the main program.
This function is executed when you type git-ew or python -m git_ew.
Parameters:
Returns:
-
int–An exit code.
Source code in src/git_ew/_internal/cli.py
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 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | |
normalize_message_id
¤
normalize_message_id(message_id: str) -> str
Normalize a message ID by removing angle brackets.
Parameters:
Returns:
-
str–Normalized message ID.
Source code in src/git_ew/_internal/email_parser.py
265 266 267 268 269 270 271 272 273 274 | |
parse_email
¤
parse_email(raw_email: str | bytes) -> ParsedEmail
Parse a raw email message.
Parameters:
Returns:
-
ParsedEmail–ParsedEmail object.
Source code in src/git_ew/_internal/email_parser.py
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 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 | |
parse_email_address
¤
parse_email_address(from_header: str) -> tuple[str, str]
Parse From header to extract name and email address.
Parameters:
-
(from_header¤str) –Raw From header value (may be "Name email@example.com" or just "email@example.com").
Returns:
Source code in src/git_ew/_internal/mailing_lists/zsh_workers/ingest.py
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | |
post_comment
async
¤
post_comment(
thread_id: int, comment: CommentCreate
) -> JSONResponse
Post a comment to a thread (sends an email).
Parameters:
-
(thread_id¤int) –Thread ID.
-
(comment¤CommentCreate) –Comment data.
Returns:
-
JSONResponse–JSON response with created message.
Source code in src/git_ew/_internal/app.py
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 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 284 285 286 287 288 289 290 291 292 293 294 295 296 | |
render_markdown
¤
Render plain text as markdown HTML.
Parameters:
Returns:
-
str–HTML string.
Source code in src/git_ew/_internal/thread_utils.py
71 72 73 74 75 76 77 78 79 80 81 | |
resolve_password
¤
Resolve a configured password.
Return None when no password is configured and required is false.
Source code in src/git_ew/_internal/secrets.py
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | |
sync_all_sources
async
¤
Sync emails from all configured sources.
Parameters:
Returns:
-
_SyncStats–Dictionary with sync statistics.
Source code in src/git_ew/_internal/sync.py
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 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 | |
sync_command
async
¤
sync_command() -> int
Run email sync as a standalone command.
Source code in src/git_ew/_internal/sync.py
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | |
sync_emails
async
¤
sync_emails() -> JSONResponse
Sync emails from configured sources.
Returns:
-
JSONResponse–JSON response with sync results.
Source code in src/git_ew/_internal/app.py
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 | |
thread_to_nested_structure
¤
thread_to_nested_structure(
roots: list[ThreadNode], *, flatten: bool = True
) -> list[_RenderedMessage]
Convert thread tree to nested structure, with single-children popped out to sibling level.
Also detects and marks quoted sections in message bodies.
Parameters:
-
(roots¤list[ThreadNode]) –List of root ThreadNodes.
-
(flatten¤bool, default:True) –Place a sole child beside its parent when true.
Returns:
-
list[_RenderedMessage]–Nested list of messages.
Source code in src/git_ew/_internal/thread_utils.py
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 | |
update_thread
async
¤
update_thread(
thread_id: int, update: ThreadUpdate
) -> JSONResponse
Update a thread.
Parameters:
-
(thread_id¤int) –Thread ID.
-
(update¤ThreadUpdate) –Update data.
Returns:
-
JSONResponse–JSON response.
Source code in src/git_ew/_internal/app.py
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 | |
view_thread
async
¤
View a specific thread with all messages.
Parameters:
-
(request¤Request) –FastAPI request.
-
(thread_id¤int) –Thread ID.
-
(flatten¤bool, default:True) –Whether to flatten linear chains.
Returns:
-
HTMLResponse–HTML response.
Source code in src/git_ew/_internal/app.py
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 | |