Skip to content
Snippets Groups Projects
Commit 753ecd2c authored by Jakob Albert's avatar Jakob Albert
Browse files

Refactor into Hypermodern Python project

parent a0086879
No related branches found
No related tags found
No related merge requests found
Showing
with 2000 additions and 84 deletions
[darglint]
strictness = short
.flake8 0 → 100644
[flake8]
select = ANN, B, B9, BLK, C, D, DAR, E, F, I, S, W
ignore = ANN101, ANN102, E203, E501, W503, D100, D103, D104
max-complexity = 10
max-line-length = 80
applications-import-names = penguin_stats
docstring-convention = google
import-order-style = google
per-file-ignores = tests/*:S101,ANN
/htmlcov/
/.coverage
/docs/_build/
/.idea/
__pycache__
/data/
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.0.1
hooks:
- id: check-yaml
- id: check-merge-conflict
- id: end-of-file-fixer
- id: fix-byte-order-marker
- id: mixed-line-ending
- id: trailing-whitespace
- repo: local
hooks:
- id: black
name: black
entry: poetry run black
language: system
types: [python]
- id: flake8
name: flake8
entry: poetry run flake8
language: system
types: [python]
......@@ -2,3 +2,11 @@
1. Server sends parameters for jacobi solver to client
2. Client sends the results back to the server after every iteration of the jacobi solver
3. Server visualizes the results
Fibonacci
---------
1. Server asks for parameter `n` = upper limit for Fibonacci numbers
2. Server sends `n` to client
3. Client calculates all Fibonacci numbers up to `n`
4. Client sends the resulting list back to the server
5. Server prints the resulting list
import zmq
import json
import random
def jacobi_iteration(u):
for i in range(1, len(u) - 1):
u[i] = 0.5 * (u[i - 1] + u[i + 1])
context = zmq.Context()
socket = context.socket(zmq.REQ)
# connect to localhost
socket.connect("tcp://127.0.0.1:5555")
print("Requesting parameters…")
socket.send(b"jacobi_request")
message = socket.recv().decode("ascii")
params = json.loads(message)
# initialize domain
u = [random.random() for i in range(params["n_unknowns"])]
u[0] = 0.0
u[len(u) - 1] = 0.0
socket.send("-".join(str(x) for x in u).encode("ascii"))
while True:
# if requested, perform jacobi iteration and send result
message = socket.recv().decode("ascii")
if message != "next":
break
jacobi_iteration(u)
socket.send("-".join(str(x) for x in u).encode("ascii"))
"""Sphinx configuration."""
project = "penguin-stats"
author = "Jakob Albert"
copyright = f"2021, {author}"
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.napoleon",
"sphinx_autodoc_typehints",
"sphinx_rtd_theme",
]
html_theme = "sphinx_rtd_theme"
FA Server Client
================
.. contents::
:local:
:backlinks: none
Server
------
.. automodule:: fa_server_client.server
:members:
Utils
-----
.. automodule:: fa_server_client.utils
:members:
Fibonacci
---------
.. automodule:: fa_server_client.fibonacci.server
:members:
.. automodule:: fa_server_client.fibonacci.client
:members:
Jacobi
------
.. automodule:: fa_server_client.jacobi.server
:members:
.. automodule:: fa_server_client.jacobi.client
:members:
[mypy]
[mypy-nox.*,zmq,matplotlib,matplotlib.pyplot]
ignore_missing_imports = True
"""Nox sessions."""
import os
import tempfile
from typing import Any
import nox
from nox.sessions import Session
locations = "src", "tests", "noxfile.py", "docs/conf.py"
nox.options.sessions = "lint", "mypy", "tests", "typeguard"
package = "penguin_stats"
def install_with_constraints(session: Session, *args: str, **kwargs: Any) -> None:
"""Install packages constrained by Poetry's lock file."""
requirements = tempfile.NamedTemporaryFile(delete=False)
session.run(
"poetry",
"export",
"--dev",
"--format=requirements.txt",
"--without-hashes",
f"--output={requirements.name}",
external=True,
)
session.install(f"--constraint={requirements.name}", *args, **kwargs)
requirements.close()
os.unlink(requirements.name)
@nox.session(python=["3.9"])
def black(session: Session) -> None:
"""Run black code formatter."""
args = session.posargs or locations
install_with_constraints(session, "black")
session.run("black", *args)
@nox.session(python=["3.9"])
def docs(session: Session) -> None:
"""Build the documentation."""
session.run("poetry", "install", "--no-dev", external=True)
install_with_constraints(
session, "sphinx", "sphinx-autodoc-typehints", "sphinx-rtd-theme"
)
session.run("sphinx-build", "docs", "docs/_build")
@nox.session(python=["3.9"])
def lint(session: Session) -> None:
"""Lint using flake8."""
args = session.posargs or locations
install_with_constraints(
session,
"flake8",
"flake8-annotations",
"flake8-bandit",
"flake8-black",
"flake8-bugbear",
"flake8-docstrings",
"flake8-import-order",
"darglint",
)
session.run("flake8", *args)
@nox.session(python=["3.9"])
def mypy(session: Session) -> None:
"""Type-check using mypy."""
args = session.posargs or locations
install_with_constraints(session, "mypy")
session.run("mypy", *args)
@nox.session(python=["3.9"])
def safety(session: Session) -> None:
"""Scan dependencies for insecure packages."""
requirements = tempfile.NamedTemporaryFile(delete=False)
session.run(
"poetry",
"export",
"--dev",
"--format=requirements.txt",
"--without-hashes",
f"--output={requirements.name}",
external=True,
)
install_with_constraints(session, "safety")
session.run("safety", "check", f"--file={requirements.name}", "--full-report")
requirements.close()
os.unlink(requirements.name)
@nox.session(python=["3.9"])
def tests(session: Session) -> None:
"""Run the test suite."""
args = session.posargs or ["--cov"]
session.run("poetry", "install", "--no-dev", external=True)
install_with_constraints(
session, "coverage[toml]", "pytest", "pytest-cov", "pytest-mock"
)
session.run("pytest", *args)
@nox.session(python=["3.9"])
def typeguard(session: Session) -> None:
"""Runtime type checking using Typeguard."""
args = session.posargs or ["-m", "not e2e"]
session.run("poetry", "install", "--no-dev", external=True)
install_with_constraints(session, "pytest", "pytest-mock", "typeguard")
session.run("pytest", f"--typeguard-packages={package}", *args)
poetry.lock 0 → 100644
This diff is collapsed.
[tool.poetry]
name = "fa-server-client"
version = "0.1.0"
description = ""
authors = [
"Samuel Kemmler <samuel.kemmler@fau.de>",
"Jakob Albert <jakob.j.albert@fau.de>",
]
[tool.poetry.dependencies]
python = "^3.9"
matplotlib = "^3.4.3"
pyzmq = "^22.3.0"
[tool.poetry.dev-dependencies]
black = "^21.9b0"
flake8 = "^3.9.2"
flake8-annotations = "^2.6.2"
flake8-bandit = "^2.1.2"
flake8-black = "^0.2.3"
flake8-bugbear = "^21.9.1"
flake8-docstrings = "^1.6.0"
flake8-import-order = "^0.18.1"
darglint = "^1.8.0"
pytest = "^6.2.5"
coverage = "^5.5"
pytest-cov = "^2.12.1"
pytest-mock = "^3.6.1"
mypy = "^0.910"
typeguard = "^2.12.1"
safety = "^1.10.3"
Sphinx = "^4.2.0"
sphinx-autodoc-typehints = "^1.12.0"
sphinx-rtd-theme = "^1.0.0"
[tool.poetry.scripts]
server = "fa_server_client.server:main"
jacobi = "fa_server_client.jacobi.client:main"
fibonacci = "fa_server_client.fibonacci.client:main"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.coverage.paths]
source = ["src", "*/site-packages"]
[tool.coverage.run]
branch = true
source = ["fa_server_client"]
[tool.coverage.report]
show_missing = true
import zmq
import json
import matplotlib.pyplot as plt
import time
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://*:5555")
while True:
# listen for requests
message = socket.recv().decode("ascii")
if message == "jacobi_request":
# create dictionary for parameters
iterations = 10
n_unknowns = 10
params = {}
params["n_unknowns"] = n_unknowns
# send parameters to client
print("Sending parameters for jacobi_request...")
socket.send(json.dumps(params).encode("ascii"))
# receive initial domain
message = socket.recv().decode("ascii").split("-")
message = [float(x) for x in message]
x_axis = [x for x in range(n_unknowns)]
plt.close()
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
line1, = ax.plot(x_axis, message)
for i in range(iterations):
# ask for next update of the unknowns and update plot
socket.send(b"next")
message = socket.recv().decode("ascii").split("-")
message = [float(x) for x in message]
line1.set_ydata(message)
fig.canvas.draw()
fig.canvas.flush_events()
time.sleep(0.5)
socket.send(b"done")
else:
print("Invalid request message.")
socket.send(b"invalid")
__version__ = "0.1.0"
from typing import List
from ..utils import connect, recv, send
def fib(n: int) -> List[int]:
f = [0, 1]
while f[-1] <= n:
f.append(f[-2] + f[-1])
return f[:-1]
def main() -> None:
print("Fibonacci: Connect to server...")
socket = connect("tcp://localhost:5555")
print("Fibonacci: Requesting parameters...")
send(socket, "fibonacci")
n = int(recv(socket))
print("Fibonacci: Calculate answer...")
answer = fib(n)
print("Fibonacci: Send answer...")
send(socket, answer)
print("Fibonacci: Await completion...")
recv(socket)
if __name__ == "__main__":
main()
import zmq
from ..utils import recv, send
def main(socket: zmq.Socket) -> None:
n = int(input("Calculate fibonacci numbers up to: "))
print("Fibonacci: Sending parameters...")
send(socket, n)
print("Fibonacci: Receiving answer...")
answer = [int(_) for _ in recv(socket)]
print(answer)
print("Fibonacci: Signal completion...")
send(socket, "done")
import random
from typing import List
from ..utils import connect, recv, send
def init(n: int) -> List[float]:
"""Initialize a List for 2D Jacobi iteration.
First and last element are fixed to zero.
Rest of the list is random.
Args:
n: Length of the initialized list.
Returns:
A initialized n-element list.
"""
# random.random() is secure enough -- ignore S311
u = [random.random() for _ in range(n)] # noqa: S311
u[0] = 0.0
u[-1] = 0.0
return u
def jacobi_iteration(u: List[float]) -> None:
"""Do one Jacobi-Iteration step."""
for i in range(1, len(u) - 1):
# Is this correct? u[i-1] will be a new value
u[i] = 0.5 * (u[i - 1] + u[i + 1])
def main() -> None:
socket = connect("tcp://localhost:5555")
print("Requesting parameters…")
send(socket, "jacobi")
params = recv(socket)
# initialize domain
u = init(params["n_unknowns"])
send(socket, u)
while True:
# if requested, perform jacobi iteration and send result
message = recv(socket)
if message != "next":
break
jacobi_iteration(u)
send(socket, u)
if __name__ == "__main__":
main()
import time
import matplotlib.pyplot as plt
import zmq
from ..utils import recv, send
def main(socket: zmq.Socket, iterations: int, n_unknowns: int) -> None:
params = {"n_unknowns": n_unknowns}
# send parameters to client
print("Sending parameters for jacobi_request...")
send(socket, params)
# receive initial domain
message = [float(_) for _ in recv(socket)]
x_axis = range(n_unknowns)
plt.close()
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
(line1,) = ax.plot(x_axis, message)
for _ in range(iterations):
# ask for next update of the unknowns and update plot
send(socket, "next")
message = [float(_) for _ in recv(socket)]
line1.set_ydata(message)
fig.canvas.draw()
fig.canvas.flush_events()
time.sleep(0.5)
send(socket, "done")
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment