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
37 changes: 37 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: CI

on:
push:
branches: [master]
pull_request:

jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v4

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

- name: Install Poetry
run: pipx install poetry

- name: Install dependencies
run: poetry install --with dev

- name: Lint
run: poetry run ruff check .

- name: Check formatting
run: poetry run ruff format --check .

- name: Run tests
run: poetry run python -m pytest tests.py
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ htmlcov/
.coverage
.coverage.*
.cache
.pytest_cache/
.ruff_cache/
nosetests.xml
coverage.xml
*,cover
Expand Down
12 changes: 0 additions & 12 deletions .travis.yml

This file was deleted.

27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Changelog

## 2.1.0

- Add a configurable `timeout` (default 60 seconds) to all network requests so
`get_bytes()`, `get_short_url()`, and `to_file()` no longer hang indefinitely.
- Drop support for end-of-life Python 3.7; the minimum supported version is now
Python 3.8. Tested against Python 3.8 through 3.13.
- Ship inline type hints and a `py.typed` marker (PEP 561).
- Derive the client `User-Agent` version from the installed package metadata so
it stays in sync with the release.
- Modernize packaging: PEP 621 `[project]` metadata, the `poetry.core.masonry.api`
build backend, and PEP 735 dependency groups.
- Replace Travis CI with GitHub Actions and `autopep8` with `ruff` for linting
and formatting.
- Remove leftover Python 2 compatibility code.

## 2.0.0

- Drop support for Python versions earlier than 3.7.
- Set a `User-Agent` header on requests.
- Show a detailed error message when chart creation fails.
- Add support for the `version` parameter.

## 1.0.1

- Last release supporting Python 2 and Python < 3.7.
29 changes: 27 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# quickchart-python
[![Build Status](https://travis-ci.com/typpo/quickchart-python.svg?branch=master)](https://travis-ci.com/typpo/quickchart-python)
[![CI](https://github.com/typpo/quickchart-python/actions/workflows/ci.yml/badge.svg)](https://github.com/typpo/quickchart-python/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/quickchart.io)](https://pypi.org/project/quickchart-io/)
[![PyPI - License](https://img.shields.io/pypi/l/quickchart.io)](https://pypi.org/project/quickchart-io/)

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

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/).
This package requires Python 3.8 or later. If you need support for Python 3.7, 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/).

# Usage

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

### timeout: float
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.

## Getting URLs

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

Checkout the `examples` directory to see other usage.

# Development

This project uses [Poetry](https://python-poetry.org/) for packaging and dependency management.

```
# Install dependencies (including dev tools)
poetry install --with dev

# Run the test suite
poetry run python -m pytest tests.py

# Lint and format
poetry run ruff check .
poetry run ruff format .
```

The tests that hit the live quickchart.io service are skipped by default. Set `QUICKCHART_NETWORK_TESTS=1` to run them:

```
QUICKCHART_NETWORK_TESTS=1 poetry run python -m pytest tests.py
```
2 changes: 1 addition & 1 deletion __init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from quickchart import *
from quickchart import * # noqa: F401,F403
33 changes: 20 additions & 13 deletions examples/discord_bot.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
from io import BytesIO

import discord
from PIL import Image
from discord.ext import commands
from PIL import Image

from quickchart import QuickChart

description = '''An example bot to showcase the use of QuickChart with discord.py module.'''
description = (
"""An example bot to showcase the use of QuickChart with discord.py module."""
)

intents = discord.Intents.default()

bot = commands.Bot(command_prefix='!', description=description, intents=intents)
bot = commands.Bot(command_prefix="!", description=description, intents=intents)


@bot.event
async def on_ready():
print(f'Logged in as {bot.user.name}')
print(f"Logged in as {bot.user.name}")


@bot.command()
Expand All @@ -27,21 +30,25 @@ async def graph(ctx):
"type": "bar",
"data": {
"labels": ["Hello world", "Test"],
"datasets": [{
"label": "Foo",
"data": [1, 2]
}]
}
"datasets": [{"label": "Foo", "data": [1, 2]}],
},
}
with Image.open(BytesIO(qc.get_bytes())) as chat_sample:
output_buffer = BytesIO() # By using BytesIO we don't have to save the file in our system.
output_buffer = (
BytesIO()
) # By using BytesIO we don't have to save the file in our system.
chat_sample.save(output_buffer, "png")
output_buffer.seek(0)
await ctx.send(file=discord.File(fp=output_buffer, filename="chart_sample.png")) # Change the file name accordingly.
await ctx.send(
file=discord.File(fp=output_buffer, filename="chart_sample.png")
) # Change the file name accordingly.


@graph.before_invoke
async def before_test_invoke(ctx):
await ctx.trigger_typing() # Take time to render and send graph so triggering typing to reflect bot action.
await (
ctx.trigger_typing()
) # Take time to render and send graph so triggering typing to reflect bot action.


bot.run('token')
bot.run("token")
16 changes: 10 additions & 6 deletions examples/gradient_fill.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@
"type": "bar",
"data": {
"labels": ["Hello world", "Test"],
"datasets": [{
"label": "Foo",
"data": [1, 2],
"backgroundColor": QuickChartFunction("getGradientFillHelper('vertical', ['rgba(63, 100, 249, 0.2)', 'rgba(255, 255, 255, 0.2)'])"),
}]
}
"datasets": [
{
"label": "Foo",
"data": [1, 2],
"backgroundColor": QuickChartFunction(
"getGradientFillHelper('vertical', ['rgba(63, 100, 249, 0.2)', 'rgba(255, 255, 255, 0.2)'])"
),
}
],
},
}

print(qc.get_url())
12 changes: 7 additions & 5 deletions examples/short_url_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
"type": "line",
"data": {
"labels": list(range(0, 100)),
"datasets": [{
"label": "Foo",
"data": random.sample(range(0, 100), 100),
}]
}
"datasets": [
{
"label": "Foo",
"data": random.sample(range(0, 100), 100),
}
],
},
}

print(qc.get_short_url())
Expand Down
4 changes: 2 additions & 2 deletions examples/short_url_example_with_function.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from quickchart import QuickChart

qc = QuickChart()
qc.config = '''{
qc.config = """{
type: 'bar',
data: {
labels: ['Q1', 'Q2', 'Q3', 'Q4'],
Expand All @@ -24,7 +24,7 @@
}]
}
}
}'''
}"""

print(qc.get_short_url())
#
Expand Down
7 changes: 2 additions & 5 deletions examples/simple_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,8 @@
"type": "bar",
"data": {
"labels": ["Hello world", "Test"],
"datasets": [{
"label": "Foo",
"data": [1, 2]
}]
}
"datasets": [{"label": "Foo", "data": [1, 2]}],
},
}

print(qc.get_url())
Expand Down
4 changes: 2 additions & 2 deletions examples/simple_example_with_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
qc.width = 600
qc.height = 300
qc.device_pixel_ratio = 2.0
qc.config = '''{
qc.config = """{
type: 'bar',
data: {
labels: ['Q1', 'Q2', 'Q3', 'Q4'],
Expand All @@ -27,6 +27,6 @@
}]
}
}
}'''
}"""

print(qc.get_url())
34 changes: 14 additions & 20 deletions examples/using_quickchartfunction.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,25 @@
"type": "bar",
"data": {
"labels": [datetime(2020, 1, 15), datetime(2021, 1, 15)],
"datasets": [{
"label": "Foo",
"data": [1, 2]
}]
"datasets": [{"label": "Foo", "data": [1, 2]}],
},
"options": {
"scales": {
"yAxes": [{
"ticks": {
"callback": QuickChartFunction('(val) => val + "k"')
}
}, {
"ticks": {
"callback": QuickChartFunction('''function(val) {
"yAxes": [
{"ticks": {"callback": QuickChartFunction('(val) => val + "k"')}},
{
"ticks": {
"callback": QuickChartFunction("""function(val) {
return val + '???';
}''')
}
}],
"xAxes": [{
"ticks": {
"callback": QuickChartFunction('(val) => "$" + val')
}
}]
}""")
}
},
],
"xAxes": [
{"ticks": {"callback": QuickChartFunction('(val) => "$" + val')}}
],
}
}
},
}

print(qc.get_url())
11 changes: 4 additions & 7 deletions examples/write_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,10 @@
"type": "bar",
"data": {
"labels": ["Hello world", "Test"],
"datasets": [{
"label": "Foo",
"data": [1, 2]
}]
}
"datasets": [{"label": "Foo", "data": [1, 2]}],
},
}

qc.to_file('/tmp/mychart.png')
qc.to_file("/tmp/mychart.png")

print('Done.')
print("Done.")
Loading
Loading