Skip to content

Commit

Permalink
Initial cookiecutter template
Browse files Browse the repository at this point in the history
  • Loading branch information
gnunicorn committed Nov 14, 2023
0 parents commit 215546d
Show file tree
Hide file tree
Showing 11 changed files with 414 additions and 0 deletions.
44 changes: 44 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Linting and Tests
on:
push:
branches: ["main"]
pull_request:

jobs:
check-code-style:
name: Check code style
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: "3.x"
- run: python -m pip install tox
- run: tox -e check_codestyle

check-types:
name: Check types with Mypy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: "3.x"
- run: python -m pip install tox
- run: tox -e check_types

unit-tests:
name: Unit tests
runs-on: ubuntu-latest
strategy:
matrix:
# Run the unit tests both against our oldest supported Python version
# and the newest stable.
python_version: [ "3.8", "3.x" ]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python_version }}
- run: python -m pip install tox
- run: tox -e py
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/.idea
/.venv
/*.egg-info
/.envrc
/.tox
_trial_temp
__pycache__
/dist
41 changes: 41 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

variables:
GIT_DEPTH: 0


check_code_style:
image: python:3
tags: ['docker']
script:
- "pip install tox"
- "tox -e check_codestyle"
rules:
- if: '$CI_PIPELINE_SOURCE == "push"'
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

check_types:
image: python:3
tags: ['docker']
script:
- "pip install tox"
- "tox -e check_types"
rules:
- if: '$CI_PIPELINE_SOURCE == "push"'
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

.unit_tests_template: &unit_tests
tags: ['docker']
script:
- "pip install tox"
- "tox -e py"
rules:
- if: '$CI_PIPELINE_SOURCE == "push"'
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

unit_tests_latest:
<<: *unit_tests
image: python:3

unit_tests_oldest:
<<: *unit_tests
image: python:3.8
97 changes: 97 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Super Inivitation flow for Synapse Matrix Homeserver

Provides extended support for users to invite other users to rooms via an inventation token

## Installation

From the virtual environment that you use for Synapse, install this module with:

```shell
pip install path/to/synapse-super-invites
```

(If you run into issues, you may need to upgrade `pip` first, e.g. by running
`pip install --upgrade pip`)

Then alter your homeserver configuration, adding to your `modules` configuration:

```yaml
modules:
- module: synapse_super_invites.SynapseSuperInvites
config:
generate_registration_tokens: true # whether or not the invite tokens are also usable as registration tokens
# TODO: Complete this section with an example for your module
```

## Development

In a virtual environment with pip ≥ 21.1, run

```shell
pip install -e .[dev]
```

To run the unit tests, you can either use:

```shell
tox -e py
```

or

```shell
trial tests
```

To run the linters and `mypy` type checker, use `./scripts-dev/lint.sh`.

## Releasing

The exact steps for releasing will vary; but this is an approach taken by the
Synapse developers (assuming a Unix-like shell):

1. Set a shell variable to the version you are releasing (this just makes
subsequent steps easier):

```shell
version=X.Y.Z
```

2. Update `setup.cfg` so that the `version` is correct.

3. Stage the changed files and commit.

```shell
git add -u
git commit -m v$version -n
```

4. Push your changes.

```shell
git push
```

5. When ready, create a signed tag for the release:

```shell
git tag -s v$version
```

Base the tag message on the changelog.

6. Push the tag.

```shell
git push origin tag v$version
```

7. If applicable:
Create a _release_, based on the tag you just pushed, on GitHub or GitLab.

8. If applicable:
Create a source distribution and upload it to PyPI:
```shell
python -m build
twine upload dist/synapse_super_invites-$version*
```
84 changes: 84 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
[project]
name = "synapse_super_invites"
description = "Provides extended support for users to invite other users to rooms via an inventation token"
readme = "README.md"

dynamic = ["version"]


requires-python = ">=3.8"

classifiers = [
"License :: OSI Approved :: Apache Software License"
]

dependencies = [
"attrs"
]

[project.optional-dependencies]
dev = [
# for tests
"matrix-synapse",
"tox",
"twisted",
"aiounittest",
# for type checking
"mypy == 1.6.1",
# for linting
"black == 23.10.0",
"ruff == 0.1.1",
]

[build-system]
requires = [
"setuptools",
"wheel",
"setuptools_scm",
]
build-backend = "setuptools.build_meta"

[tool.setuptools_scm]

[tool.mypy]
strict = true

[tool.ruff]
line-length = 88

# See https://docs.astral.sh/ruff/rules/#error-e
# for error codes. The ones we ignore are:
# E501: Line too long (black enforces this for us)
# E731: do not assign a lambda expression, use a def
#
# flake8-bugbear compatible checks. Its error codes are described at
# https://docs.astral.sh/ruff/rules/#flake8-bugbear-b
# B023: Functions defined inside a loop must not use variables redefined in the loop
ignore = [
"B023",
"E501",
"E731",
]
select = [
# pycodestyle
"E",
"W",
# pyflakes
"F",
# flake8-bugbear
"B0",
# flake8-comprehensions
"C4",
# flake8-2020
"YTT",
# flake8-slots
"SLOT",
# flake8-debugger
"T10",
# flake8-pie
"PIE",
# flake8-executable
"EXE",
# isort
"I",
]
19 changes: 19 additions & 0 deletions scripts-dev/lint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
# Runs linting scripts and type checking
# black - opinionated code formatter
# ruff - lints, finds mistakes, and sorts import statements
# mypy - checks type annotations

set -e

files=(
"synapse_super_invites"
"tests"
)

# Print out the commands being run
set -x

black "${files[@]}"
ruff --fix "${files[@]}"
mypy "${files[@]}"
32 changes: 32 additions & 0 deletions synapse_super_invites/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from typing import Any, Dict

import attr
from synapse.module_api import ModuleApi


@attr.s(auto_attribs=True, frozen=True)
class SynapseSuperInvitesConfig:
pass


class SynapseSuperInvites:
def __init__(self, config: SynapseSuperInvitesConfig, api: ModuleApi):
# Keep a reference to the config and Module API
self._api = api
self._config = config

@staticmethod
def parse_config(config: Dict[str, Any]) -> SynapseSuperInvitesConfig:
# Parse the module's configuration here.
# If there is an issue with the configuration, raise a
# synapse.module_api.errors.ConfigError.
#
# Example:
#
# some_option = config.get("some_option")
# if some_option is None:
# raise ConfigError("Missing option 'some_option'")
# if not isinstance(some_option, str):
# raise ConfigError("Config option 'some_option' must be a string")
#
return SynapseSuperInvitesConfig()
Empty file added synapse_super_invites/py.typed
Empty file.
56 changes: 56 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from asyncio import Future
from typing import Any, Awaitable, Dict, Optional, TypeVar
from unittest.mock import Mock

import attr
from synapse.module_api import ModuleApi

from synapse_super_invites import SynapseSuperInvites

TV = TypeVar("TV")


def make_awaitable(result: TV) -> Awaitable[TV]:
"""
Makes an awaitable, suitable for mocking an `async` function.
This uses Futures as they can be awaited multiple times so can be returned
to multiple callers.
This function has been copied directly from Synapse's tests code.
"""
future = Future() # type: ignore
future.set_result(result)
return future


@attr.s(auto_attribs=True)
class MockEvent:
"""Mocks an event. Only exposes properties the module uses."""

sender: str
type: str
content: Dict[str, Any]
room_id: str = "!someroom"
state_key: Optional[str] = None

def is_state(self) -> bool:
"""Checks if the event is a state event by checking if it has a state key."""
return self.state_key is not None

@property
def membership(self) -> str:
"""Extracts the membership from the event. Should only be called on an event
that's a membership event, and will raise a KeyError otherwise.
"""
membership: str = self.content["membership"]
return membership


def create_module() -> SynapseSuperInvites:
# Create a mock based on the ModuleApi spec, but override some mocked functions
# because some capabilities are needed for running the tests.
module_api = Mock(spec=ModuleApi)

# If necessary, give parse_config some configuration to parse.
config = SynapseSuperInvites.parse_config({})

return SynapseSuperInvites(config, module_api)
6 changes: 6 additions & 0 deletions tests/test_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import aiounittest


class ExampleTest(aiounittest.AsyncTestCase):
async def test_example(self) -> None:
self.assertEqual(1, 2 - 1)
Loading

0 comments on commit 215546d

Please sign in to comment.