Skip to content
Merged
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
3 changes: 1 addition & 2 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ jobs:

- name: Build release distributions
run: |
# NOTE: put your own distribution build steps here.
python -m pip install build
python -m build

Expand Down Expand Up @@ -67,4 +66,4 @@ jobs:
- name: Publish release distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist/
packages-dir: dist/
56 changes: 56 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Tests

on:
push:
pull_request:

jobs:
unittest:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version:
- "3.8"
- "3.9"
- "3.10"
- "3.11"
- "3.12"
- "3.13"
- "3.14"

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install package
run: |
python -m pip install --upgrade pip
python -m pip install -r requirements.txt -e .

- name: Run tests
run: |
python -m unittest discover -s tests
python -m compileall ytelegraph tests

uv:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.14"

- name: Install uv
run: python -m pip install uv

- name: Run tests with uv
run: |
uv --version
uv run --with-requirements requirements.txt python -m unittest discover -s tests
uv run --with-requirements requirements.txt python -m compileall ytelegraph tests
67 changes: 67 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Repository Guidelines

## Project Structure & Module Organization

YTelegraph is a small Python package for publishing Markdown and DOM content through the Telegraph API.

- `ytelegraph/` contains the package code: `api.py` for page operations, `account.py` for token/account handling, and `md_to_dom.py` for Markdown conversion.
- `examples/` contains runnable usage examples for creating, editing, and inspecting Telegraph pages.
- `draft_tests/` contains exploratory/manual integration scripts. Many call the live Telegraph API or wait for user input.
- `setup.py`, `requirements.txt`, `README.md`, and `CHANGELOG.md` define packaging, dependencies, usage docs, and release history.

## Build, Test, and Development Commands

This project historically uses plain pip/setuptools. Use uv as an additional compatibility path, not as the source of truth.

```bash
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt -e .
```

Install the package in editable mode with runtime dependencies.

```bash
python -m unittest discover -s tests
python -m compileall ytelegraph tests
python -m pip install build
python -m build
```

Use `compileall` as a quick syntax check. Use `python -m build` to verify source and wheel distributions; install `build` first if needed.

Run the uv compatibility path intentionally:

```bash
uv run --with-requirements requirements.txt python -m unittest discover -s tests
uv run --with-requirements requirements.txt python -m compileall ytelegraph tests
uv run --with build --with-requirements requirements.txt python -m build
```

```bash
python examples/basic_usage.py
python examples/second_usage.py
python draft_tests/test_space_missing.py
```

Run examples and draft tests intentionally because they may create or edit live Telegraph pages.

## Coding Style & Naming Conventions

Use Python 3.8-compatible syntax, 4-space indentation, and clear type hints for public interfaces. Keep module names lowercase with underscores. Follow existing API naming: Markdown helpers end in `_md`, append helpers use explicit names such as `edit_page_md_append_to_front`, and internal helpers are prefixed with `_`.

Keep docstrings concise and focused on behavior, arguments, returns, and exceptions. Avoid broad refactors when fixing targeted API behavior.

## Testing Guidelines

The `tests/` directory contains deterministic unit tests. Treat `draft_tests/` as integration/manual coverage and add isolated tests when changing parsing or path-handling logic. New tests should be named `test_<behavior>.py`; prefer deterministic tests for `md_to_dom` and API payload behavior that do not require network access. For live API checks, use disposable pages and avoid relying on existing public page paths.

## Commit & Pull Request Guidelines

Git history uses short messages, often with Conventional Commit prefixes such as `feat:`, `fix:`, and `chore:`. Prefer that style: `fix: preserve spaces in markdown formatting`.

Pull requests should include a short problem summary, the implementation approach, commands run, and any live Telegraph API effects. Link related issues when available. Include screenshots or page URLs only when they help verify user-visible publishing behavior.

## Security & Configuration Tips

Never commit Telegraph access tokens or generated `ph_token.txt` files. Use `TELEGRA_PH_TOKEN` for an access token, and `PH_TOKEN_PATH` or a local ignored token file for manual testing.
21 changes: 20 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Added

- Added deterministic unit tests for Markdown conversion, path handling, payload construction, token loading, and page retrieval behavior.
- Added CI coverage for pip and uv workflows across current Python versions.
- Added automatic `TELEGRA_PH_TOKEN` support.

### Changed

- Updated packaging classifiers through Python 3.14.
- Preserved Telegraph page response fields from `get_page`.

### Fixed

- Fixed `delete_page` verification.
- Added request timeouts for Telegraph API calls.
- Stopped printing newly created access tokens in account creation output and examples.

## [0.2.1] - 2025-03-22

### Fixed
Expand Down Expand Up @@ -63,4 +82,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Security

- N/A
- N/A
30 changes: 26 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
[![PyPi Package Version](https://img.shields.io/pypi/v/your-telegraph.svg)](https://pypi.python.org/pypi/your-telegraph)
[![Supported Python versions](https://img.shields.io/pypi/pyversions/your-telegraph.svg)](https://pypi.python.org/pypi/your-telegraph)
[![PyPi downloads](https://img.shields.io/pypi/dm/your-telegraph.svg)](https://pypi.org/project/your-telegraph/)
[![PyPI Downloads](https://static.pepy.tech/personalized-badge/your-telegraph?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/your-telegraph)
[![PyPi status](https://img.shields.io/pypi/status/your-telegraph.svg?style=flat-square)](https://pypi.python.org/pypi/your-telegraph)
![License](https://img.shields.io/github/license/alterxyz/ytelegraph)

Expand All @@ -22,7 +23,7 @@ YTelegraph is a simple, user-friendly Python wrapper for the Telegraph API. Publ
- [Advanced Usage](#advanced-usage)
- [Token Management](#token-management)
- [Create Account](#create-account)
- [Testing](#testing)
- [Development and Testing](#development-and-testing)
- [Versioning](#versioning)
- [Support](#support)
- [Contributing](#contributing)
Expand Down Expand Up @@ -93,7 +94,7 @@ This method is useful if you want to use an existing Telegraph account or manage

### Advanced Usage

Try and see the `example/second_usage.py` at [here](examples/second_usage.py).
Try and see the `examples/second_usage.py` at [here](examples/second_usage.py).

## Token Management

Expand All @@ -102,6 +103,7 @@ YTelegraph offers flexible token management:
1. **Automatic**: If no token is provided, YTelegraph creates a new account and manages the token for you.
2. **Environment Variable**: Set the `TELEGRA_PH_TOKEN` environment variable, and YTelegraph will use it automatically.
3. **Direct Input**: Pass your token directly to the `TelegraphAPI` constructor.
4. **Token file path**: Set `PH_TOKEN_PATH` if you want the automatically created token stored somewhere other than the default token file.

Choose the method that best fits your workflow and security requirements.

Expand Down Expand Up @@ -137,9 +139,29 @@ For more details, check out the [Telegraph API documentation](https://telegra.ph

YTelegraph makes this process super easy, but it's good to know how to do it manually if you ever need to.

## Testing
## Development and Testing

To run the basic integration tests, execute the examples in the `examples/` directory:
This project still uses the small setuptools/pip workflow it started with:

```bash
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt -e .
python -m unittest discover -s tests
python -m compileall ytelegraph tests
python -m pip install build
python -m build
```

You can also test the package with uv without converting the project to uv:

```bash
uv run --with-requirements requirements.txt python -m unittest discover -s tests
uv run --with-requirements requirements.txt python -m compileall ytelegraph tests
uv run --with build --with-requirements requirements.txt python -m build
```

The examples in `examples/` and scripts in `draft_tests/` are live Telegraph API checks. Run them intentionally because they can create or edit Telegraph pages:

```bash
python examples/basic_usage.py
Expand Down
3 changes: 2 additions & 1 deletion examples/second_usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
my_token_file_path = my_ph._get_token_file_path()

my_token = my_ph._get_token()
masked_token = f"{my_token[:4]}...{my_token[-4:]}" if my_token else None

my_info = my_ph.get_account_info()

print(
f"\nYour token file path: {my_token_file_path}\nYour token: {my_token}\nYour account info: {my_info}"
f"\nYour token file path: {my_token_file_path}\nYour token: {masked_token}\nYour account info: {my_info}"
)

# login to your account by open the link in your browser
Expand Down
8 changes: 5 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
url="https://github.com/alterxyz/ytelegraph",
packages=find_packages(),
install_requires=[
"requests",
"beautifulsoup4",
"Markdown",
"requests>=2.32.3,<3.0.0",
"beautifulsoup4>=4.12.3,<5.0.0",
"Markdown>=3.6,<4.0.0",
],
classifiers=[
"Development Status :: 3 - Alpha",
Expand All @@ -28,6 +28,8 @@
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Operating System :: OS Independent",
"Topic :: Internet :: WWW/HTTP",
"Topic :: Software Development :: Libraries :: Python Modules",
Expand Down
64 changes: 64 additions & 0 deletions tests/test_account.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import json
import os
import tempfile
import unittest
from unittest.mock import patch

from ytelegraph.account import TelegraphAccount


class FakeResponse:
def __init__(self, payload):
self.payload = payload

def raise_for_status(self) -> None:
pass

def json(self):
return self.payload


class TelegraphAccountTests(unittest.TestCase):
def test_prefers_environment_token(self) -> None:
with patch.dict(os.environ, {"TELEGRA_PH_TOKEN": "env-token"}):
with patch.object(TelegraphAccount, "get_account_info", return_value={}):
with patch.object(TelegraphAccount, "_get_token") as get_token:
with patch.object(TelegraphAccount, "_create_account") as create_account:
account = TelegraphAccount()

self.assertEqual(account.access_token, "env-token")
get_token.assert_not_called()
create_account.assert_not_called()

def test_get_account_info_serializes_fields_and_uses_timeout(self) -> None:
account = TelegraphAccount.__new__(TelegraphAccount)
account.base_url = "https://api.telegra.ph"
account.access_token = "token"
account.request_timeout = 3.0

with patch("ytelegraph.account.requests.get") as mock_get:
mock_get.return_value = FakeResponse(
{"ok": True, "result": {"short_name": "Sandbox", "page_count": 1}}
)

result = account.get_account_info(["short_name", "page_count"])

self.assertEqual(result["page_count"], 1)
_, kwargs = mock_get.call_args
self.assertEqual(kwargs["timeout"], 3.0)
self.assertEqual(json.loads(kwargs["params"]["fields"]), ["short_name", "page_count"])

def test_save_token_uses_configured_path(self) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
token_path = os.path.join(temp_dir, "token.txt")
account = TelegraphAccount.__new__(TelegraphAccount)

with patch.dict(os.environ, {"PH_TOKEN_PATH": token_path}):
account._save_token("secret-token")

with open(token_path, "r", encoding="utf-8") as token_file:
self.assertEqual(token_file.read(), "secret-token")


if __name__ == "__main__":
unittest.main()
Loading
Loading