Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
*.csv
.vscode
q_and_a
/*
**/*.egg-info/
**/__pycache__/
!assets/
!resources/
!src/
!tests/
!.gitignore
!LICENSE
!pyproject.toml
!README.md
!release.ps1
!requirements.txt
2 changes: 2 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
MIT License

Copyright (c) 2024 Wei Cheng

Copyright (c) 2024 Mahmoud Abdelkhalek

Permission is hereby granted, free of charge, to any person obtaining a copy
Expand Down
413 changes: 250 additions & 163 deletions README.md

Large diffs are not rendered by default.

Binary file added assets/account_id.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed assets/address_bar_userid.png
Binary file not shown.
Binary file added assets/demo.avif
Binary file not shown.
Binary file removed assets/example1.png
Binary file not shown.
Binary file removed assets/example2.png
Binary file not shown.
Binary file removed assets/example3.png
Binary file not shown.
Binary file added assets/markdown.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/network_user.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed assets/se_backup.gif
Binary file not shown.
Binary file removed assets/se_click.png
Binary file not shown.
403 changes: 0 additions & 403 deletions main.py

This file was deleted.

212 changes: 212 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
[build-system]
requires = ["setuptools >= 78.1.1"]
build-backend = "setuptools.build_meta"

[project]
name = "stack-exchange-backup"
version = "2026.02.25"
description = "Download all your posts on the Stack Exchange network as Markdown files."
readme = "./README.md"
requires-python = ">= 3.12"
license = "MIT"
license-files = ["./LICENSE"]
authors = [{ name = "Wei Cheng", email = "weicheng018@gmail.com" }]
keywords = [
"Stack Exchange",
]
classifiers = [
"Development Status :: 3 - Alpha",
# "Development Status :: 4 - Beta",
# "Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Intended Audience :: End Users/Desktop",
"Operating System :: OS Independent",
"Operating System :: Microsoft :: Windows",
# "Operating System :: MacOS :: MacOS X",
"Operating System :: POSIX :: Linux",
# "Operating System :: POSIX :: BSD",
# "Operating System :: POSIX :: SunOS/Solaris",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
# "Programming Language :: Python :: 3.15",
"Programming Language :: Python :: Implementation :: CPython",
# "Programming Language :: Python :: Implementation :: PyPy",
"Programming Language :: Python :: Implementation :: GraalPy",
"Topic :: System :: Archiving :: Backup",
]
urls.source = "https://github.com/9ao9ai9ar/stack-exchange-backup"
dependencies = [
"attrs >= 25.4.0", # https://www.attrs.org/en/stable/changelog.html
"cattrs[pyyaml] >= 25.3.0", # https://catt.rs/en/stable/history.html
"requests >= 2.32.4", # https://requests.readthedocs.io/en/latest/community/updates/#release-history
"ruamel.yaml >= 0.18.16", # https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree/CHANGES
]

[dependency-groups]
dev = [
"datamodel-code-generator >= 0.54.0", # https://github.com/koxudaxi/datamodel-code-generator/releases
"openapi-spec-validator >= 0.8.1", # https://github.com/python-openapi/openapi-spec-validator/releases
"pylint >= 4.0.5, < 5", # https://pylint.readthedocs.io/en/latest/whatsnew/4/index.html
"ruff >= 0.15.2", # https://github.com/astral-sh/ruff/releases
]
sit = [
"pytest >= 9.0.0, < 10", # https://docs.pytest.org/en/stable/changelog.html
]
uat = [
"memory-profiler >= 0.61.0",
"memray >= 1.19.1 ; sys_platform == 'linux' or sys_platform == 'darwin'", # https://bloomberg.github.io/memray/changelog.html
]
prod = [
"bumpver >= 2025.1131", # https://github.com/mbarkhau/bumpver/blob/master/CHANGELOG.md
"validate-pyproject[all] >= 0.25", # https://validate-pyproject.readthedocs.io/en/latest/changelog.html
]
all = [
{ include-group = "dev" },
{ include-group = "sit" },
{ include-group = "uat" },
{ include-group = "prod" },
]

# https://github.com/mbarkhau/bumpver?tab=readme-ov-file#configuration
[tool.bumpver]
current_version = "2026.02.25"
version_pattern = "YYYY.0M.0D[-PATCH]"
commit = false

[tool.bumpver.file_patterns]
"pyproject.toml" = [
'^current_version = "{version}"',
'^version = "{version}"',
]

# https://koxudaxi.github.io/datamodel-code-generator/cli-reference/quick-reference/
[tool.datamodel-codegen]
additional-imports = "attr.dataclass"
formatters = [
"isort",
"ruff-check",
"ruff-format",
]
input = "./resources/openapi/openapi.yaml"
input-file-type = "openapi"
output = "./src/stackexchange/generated/_model_openapi.py"
output-model-type = "dataclasses.dataclass"
## Typing customization:
enum-field-as-literal = "all"
## Field customization:
use-field-description = true
## Model customization:
collapse-root-models = true
disable-timestamp = true
keep-model-order = true
keyword-only = true
use-exact-imports = true
use-schema-description = true
## Template customization:
encoding = "utf-8"
use-double-quotes = true
## OpenAPI-only options:
openapi-scopes = [
"schemas",
"parameters",
"paths",
]
use-operation-id-as-name = true

# https://pylint.readthedocs.io/en/latest/user_guide/messages/messages_overview.html
[tool.pylint.main]
disable = [
"missing-class-docstring",
"missing-function-docstring",
"missing-module-docstring",
"no-else-return",
"unused-argument",
"unused-wildcard-import",
"wildcard-import",
]
enable = [
"useless-suppression",
]
fail-under = 9
ignore-paths = [
".*/generated",
]
jobs = 0
load-plugins = [
"pylint.extensions.code_style",
]
output-format = "colorized"

[tool.pylint.design]
max-args = 5 # Default
max-attributes = 7 # Default
max-locals = 15 # Default
min-public-methods = 2 # Default

[tool.pylint.format]
max-line-length = 99

# https://microsoft.github.io/pyright/#/configuration?id=type-check-diagnostics-settings
[tool.pyright]
include = [
"./src",
"./tests",
]
exclude = [
"**/__pycache__",
]
typeCheckingMode = "strict"
deprecateTypingAliases = true
reportUnnecessaryTypeIgnoreComment = true
# "error" in "strict" mode
reportUnknownParameterType = false
reportUnknownArgumentType = false
reportUnknownLambdaType = false
reportUnknownVariableType = false
reportUnknownMemberType = false
reportMissingParameterType = false
reportMissingTypeArgument = false

# https://docs.pytest.org/en/stable/reference/reference.html#configuration-options
[tool.pytest]
addopts = [
"-vvr aP",
"--tb=short",
"--capture=sys",
]
strict = true
testpaths = ["./tests"]

# https://docs.astral.sh/ruff/settings/
[tool.ruff]
line-length = 79
extend-exclude = [
"**/generated",
]

[tool.ruff.lint]
ignore = [
"F403", # undefined-local-with-import-star
"F405", # undefined-local-with-import-star-usage
] # https://docs.astral.sh/ruff/rules/

# https://docs.astral.sh/uv/reference/settings/
[tool.uv]
native-tls = true

[tool.uv.pip]
emit-index-url = true
generate-hashes = true
link-mode = "copy" # https://github.com/astral-sh/uv/issues/7918
no-binary = ["stack-exchange-backup"]
#no-build = true # prefer-binary: https://github.com/astral-sh/uv/issues/1794
reinstall = true
#require-hashes = true # Doesn't work for editable installs: https://github.com/pypa/pip/issues/4995
strict = true
universal = true
upgrade = true
verify-hashes = true
44 changes: 44 additions & 0 deletions release.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/bin/sh
#Requires -Version 7

# This is a polyglot script that runs in both a POSIX compliant shell and PowerShell 7+.
# It assumes the following prerequisites have been met:
# 1. uv is installed via the standalone installer and in PATH
# 2. Node.js is installed and in PATH
# 3. The current working directory is set to the project root
# 4. The environment variable SC_GITHUB_TOKEN is set to a valid GitHub token
# 5. A compatible Python virtual environment is activated
# The shell commands are separated into blocks by the stages in the software testing life cycle:
# 1. development
# 1.1. dependency management
# 1.2. code generation
# 1.3. linting
# 2. system integration testing
# 3. user acceptance testing
# 4. production

uv self update &&
uv tool install --upgrade security-constraints &&
npm install pyright@latest &&
uv tool run security-constraints --min-severity moderate --output ./constraints.txt &&
uv pip compile --constraints ./constraints.txt --output-file ./requirements.txt ./pyproject.toml &&
uv pip sync ./requirements.txt &&
uv pip install --editable . --constraints ./constraints.txt --constraints ./requirements.txt --group all &&

python -m openapi_spec_validator --subschema-errors all --validation-errors all ./resources/openapi/openapi.yaml &&
python -m datamodel_code_generator &&

python -m ruff check &&
python -m pylint ./src/ ./tests/ &&
npm exec pyright &&

python -m pytest &&

python -m mprof run --include-children ./src/stackexchange/backup.py --account-id 8 &&
python -m mprof peak &&
python -m mprof clean &&

python -m bumpver update --patch --no-fetch &&
python -m validate_pyproject ./pyproject.toml &&

$(exit)
Loading