Skip to content

Commit c0348b2

Browse files
authored
feat!: modernize package for Python 3.9+
BREAKING CHANGE: Python 3.9 or newer is required. Adds request timeouts, typed package metadata, GitHub Actions CI, and Ruff tooling.
1 parent dbb4825 commit c0348b2

20 files changed

Lines changed: 561 additions & 281 deletions

.github/workflows/ci.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [master]
6+
pull_request:
7+
8+
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
strategy:
12+
fail-fast: false
13+
matrix:
14+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Set up Python ${{ matrix.python-version }}
20+
uses: actions/setup-python@v5
21+
with:
22+
python-version: ${{ matrix.python-version }}
23+
24+
- name: Install Poetry
25+
run: pipx install poetry
26+
27+
- name: Install dependencies
28+
run: poetry install --with dev
29+
30+
- name: Lint
31+
run: poetry run ruff check .
32+
33+
- name: Check formatting
34+
run: poetry run ruff format --check .
35+
36+
- name: Run tests
37+
run: poetry run python -m pytest tests.py

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ htmlcov/
8080
.coverage
8181
.coverage.*
8282
.cache
83+
.pytest_cache/
84+
.ruff_cache/
8385
nosetests.xml
8486
coverage.xml
8587
*,cover

.travis.yml

Lines changed: 0 additions & 12 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Changelog
2+
3+
## 3.0.0
4+
5+
- Add a configurable `timeout` (default 60 seconds) to all network requests so
6+
`get_bytes()`, `get_short_url()`, and `to_file()` no longer hang indefinitely.
7+
- Drop support for end-of-life Python 3.7 and 3.8; the minimum supported version
8+
is now Python 3.9. Tested against Python 3.9 through 3.13.
9+
- Ship inline type hints and a `py.typed` marker (PEP 561).
10+
- Derive the client `User-Agent` version from the installed package metadata so
11+
it stays in sync with the release.
12+
- Modernize packaging: PEP 621 `[project]` metadata, the `poetry.core.masonry.api`
13+
build backend, and PEP 735 dependency groups.
14+
- Replace Travis CI with GitHub Actions and `autopep8` with `ruff` for linting
15+
and formatting.
16+
- Remove leftover Python 2 compatibility code.
17+
18+
## 2.0.0
19+
20+
- Drop support for Python versions earlier than 3.7.
21+
- Set a `User-Agent` header on requests.
22+
- Show a detailed error message when chart creation fails.
23+
- Add support for the `version` parameter.
24+
25+
## 1.0.1
26+
27+
- Last release supporting Python 2 and Python < 3.7.

README.md

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# quickchart-python
2-
[![Build Status](https://travis-ci.com/typpo/quickchart-python.svg?branch=master)](https://travis-ci.com/typpo/quickchart-python)
2+
[![CI](https://github.com/typpo/quickchart-python/actions/workflows/ci.yml/badge.svg)](https://github.com/typpo/quickchart-python/actions/workflows/ci.yml)
33
[![PyPI](https://img.shields.io/pypi/v/quickchart.io)](https://pypi.org/project/quickchart-io/)
44
[![PyPI - License](https://img.shields.io/pypi/l/quickchart.io)](https://pypi.org/project/quickchart-io/)
55

@@ -13,7 +13,7 @@ Use the `quickchart` library in this project, or install through [pip](https://p
1313
pip install quickchart.io
1414
```
1515

16-
As of release 2.0, this package requires >= Python 3.7. If you need support for earlier versions of Python, use [version 1.0.1](https://pypi.org/project/quickchart-io/1.0.1/).
16+
Version 3.0.0 and later require Python 3.9 or later. If you need support for Python 3.7 or 3.8, use [version 2.0.0](https://pypi.org/project/quickchart-io/2.0.0/); for earlier versions of Python, use [version 1.0.1](https://pypi.org/project/quickchart-io/1.0.1/).
1717

1818
# Usage
1919

@@ -132,6 +132,9 @@ Override the host of the chart render server. Defaults to quickchart.io.
132132
### key: str
133133
Set an API key that will be included with the request.
134134

135+
### timeout: float
136+
Timeout in seconds for the network requests made by `get_bytes()`, `get_short_url()`, and `to_file()`. Defaults to 60.0. Set to `None` to disable the timeout.
137+
135138
## Getting URLs
136139

137140
There are two ways to get a URL for your chart object.
@@ -159,3 +162,25 @@ Writes the chart image to a file path.
159162
## More examples
160163

161164
Checkout the `examples` directory to see other usage.
165+
166+
# Development
167+
168+
This project uses [Poetry](https://python-poetry.org/) for packaging and dependency management.
169+
170+
```
171+
# Install dependencies (including dev tools)
172+
poetry install --with dev
173+
174+
# Run the test suite
175+
poetry run python -m pytest tests.py
176+
177+
# Lint and format
178+
poetry run ruff check .
179+
poetry run ruff format .
180+
```
181+
182+
The tests that hit the live quickchart.io service are skipped by default. Set `QUICKCHART_NETWORK_TESTS=1` to run them:
183+
184+
```
185+
QUICKCHART_NETWORK_TESTS=1 poetry run python -m pytest tests.py
186+
```

__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from quickchart import *
1+
from quickchart import * # noqa: F401,F403

examples/discord_bot.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
from io import BytesIO
22

33
import discord
4-
from PIL import Image
54
from discord.ext import commands
5+
from PIL import Image
6+
67
from quickchart import QuickChart
78

8-
description = '''An example bot to showcase the use of QuickChart with discord.py module.'''
9+
description = (
10+
"""An example bot to showcase the use of QuickChart with discord.py module."""
11+
)
912

1013
intents = discord.Intents.default()
1114

12-
bot = commands.Bot(command_prefix='!', description=description, intents=intents)
15+
bot = commands.Bot(command_prefix="!", description=description, intents=intents)
1316

1417

1518
@bot.event
1619
async def on_ready():
17-
print(f'Logged in as {bot.user.name}')
20+
print(f"Logged in as {bot.user.name}")
1821

1922

2023
@bot.command()
@@ -27,21 +30,25 @@ async def graph(ctx):
2730
"type": "bar",
2831
"data": {
2932
"labels": ["Hello world", "Test"],
30-
"datasets": [{
31-
"label": "Foo",
32-
"data": [1, 2]
33-
}]
34-
}
33+
"datasets": [{"label": "Foo", "data": [1, 2]}],
34+
},
3535
}
3636
with Image.open(BytesIO(qc.get_bytes())) as chat_sample:
37-
output_buffer = BytesIO() # By using BytesIO we don't have to save the file in our system.
37+
output_buffer = (
38+
BytesIO()
39+
) # By using BytesIO we don't have to save the file in our system.
3840
chat_sample.save(output_buffer, "png")
3941
output_buffer.seek(0)
40-
await ctx.send(file=discord.File(fp=output_buffer, filename="chart_sample.png")) # Change the file name accordingly.
42+
await ctx.send(
43+
file=discord.File(fp=output_buffer, filename="chart_sample.png")
44+
) # Change the file name accordingly.
4145

4246

4347
@graph.before_invoke
4448
async def before_test_invoke(ctx):
45-
await ctx.trigger_typing() # Take time to render and send graph so triggering typing to reflect bot action.
49+
await (
50+
ctx.trigger_typing()
51+
) # Take time to render and send graph so triggering typing to reflect bot action.
52+
4653

47-
bot.run('token')
54+
bot.run("token")

examples/gradient_fill.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@
88
"type": "bar",
99
"data": {
1010
"labels": ["Hello world", "Test"],
11-
"datasets": [{
12-
"label": "Foo",
13-
"data": [1, 2],
14-
"backgroundColor": QuickChartFunction("getGradientFillHelper('vertical', ['rgba(63, 100, 249, 0.2)', 'rgba(255, 255, 255, 0.2)'])"),
15-
}]
16-
}
11+
"datasets": [
12+
{
13+
"label": "Foo",
14+
"data": [1, 2],
15+
"backgroundColor": QuickChartFunction(
16+
"getGradientFillHelper('vertical', ['rgba(63, 100, 249, 0.2)', 'rgba(255, 255, 255, 0.2)'])"
17+
),
18+
}
19+
],
20+
},
1721
}
1822

1923
print(qc.get_url())

examples/short_url_example.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
"type": "line",
88
"data": {
99
"labels": list(range(0, 100)),
10-
"datasets": [{
11-
"label": "Foo",
12-
"data": random.sample(range(0, 100), 100),
13-
}]
14-
}
10+
"datasets": [
11+
{
12+
"label": "Foo",
13+
"data": random.sample(range(0, 100), 100),
14+
}
15+
],
16+
},
1517
}
1618

1719
print(qc.get_short_url())

examples/short_url_example_with_function.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from quickchart import QuickChart
22

33
qc = QuickChart()
4-
qc.config = '''{
4+
qc.config = """{
55
type: 'bar',
66
data: {
77
labels: ['Q1', 'Q2', 'Q3', 'Q4'],
@@ -24,7 +24,7 @@
2424
}]
2525
}
2626
}
27-
}'''
27+
}"""
2828

2929
print(qc.get_short_url())
3030
#

0 commit comments

Comments
 (0)