Skip to content

Commit

Permalink
Merge pull request #23 from Aharoni-Lab/linting
Browse files Browse the repository at this point in the history
Linting - add Ruff and Black
  • Loading branch information
sneakers-the-rat authored Jun 22, 2024
2 parents 44ae9ea + 54690b6 commit 56f821b
Show file tree
Hide file tree
Showing 33 changed files with 1,130 additions and 511 deletions.
17 changes: 17 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Lint

on:
push:

jobs:
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: chartboost/ruff-action@v1

black:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: psf/black@stable
1 change: 0 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ name: Run Python tests

on:
push:
pull_request:
workflow_dispatch:

jobs:
Expand Down
10 changes: 10 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.10
hooks:
- id: ruff
args: [ --fix ]
- repo: https://github.com/psf/black
rev: 24.1.1
hooks:
- id: black
10 changes: 10 additions & 0 deletions docs/meta/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

## 0.2

### 0.2.1 - 24-06-21

Linting and code formatting :)

Added `black` and `ruff` for linting and formatting,
reformatted the package.

See the [Contributing](./contributing.md) documentation for
details and usage.

### 0.2.0 - 24-06-21

StreamDaq enhancements and testing
Expand Down
71 changes: 71 additions & 0 deletions docs/meta/contributing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Contributing

Standard flow:
- Fork the repository
- Create a new branch from `main`
- ~ do work ~
- Open pull request against `miniscope-io:main`
- Code review and discussion happens
- Merge contribution

normal i guess.

## Norms

- All new code should be tested if possible. Since this is a hardware
interface package, some things may be impossible to test. For any
major new hardware functionality, a mock class should be written
to isolate software and hardware testing.
- All modifications to code should be documented: this includes both
API documentation in docstrings as well as narrative usage documentation
in the `docs` directory.

## Code of Conduct

(forthcoming, for now BDFLs enforce kindness and inclusiveness with their
arbitrary and expansive power)

## Development Environment

Install using the `dev` extra, which should have all other extras in it

```shell
poetry install --extras dev
# or
pip install '.[dev]'
```

### Linting

`miniscope-io` uses `black` for code formatting and `ruff` for linting.
We recommend you configure your IDE to do both automatically.

There are a few ways you can run linting manually:

First, just by running the raw commands:

```shell
ruff check --fix
black miniscope_io
```

Or you can use the `pre-commit` action to automatically run them
before committing code:

```shell
pre-commit install
```

Or you can use the `noxfile.py`

```shell
# just lint without formatting
nox -s lint
# lint and apply formatting
nox -s format
```





1 change: 1 addition & 0 deletions docs/meta/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

```{toctree}
changelog
contributing
todo
```

28 changes: 16 additions & 12 deletions miniscope_io/__init__.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
"""
I/O SDK for UCLA Miniscopes
"""

from pathlib import Path

from miniscope_io.models.config import Config
from miniscope_io.logging import init_logger
from miniscope_io.io import SDCard
from miniscope_io.logging import init_logger
from miniscope_io.models.config import Config

BASE_DIR = Path(__file__).parent.resolve()
DATA_DIR = BASE_DIR / 'data'
CONFIG_DIR = DATA_DIR / 'config'
DEVICE_DIR = BASE_DIR / 'devices'
DATA_DIR = BASE_DIR / "data"
CONFIG_DIR = DATA_DIR / "config"
DEVICE_DIR = BASE_DIR / "devices"

__all__ = [
'BASE_DIR',
'DATA_DIR',
'CONFIG_DIR',
'Config',
'SDCard',
'init_logger',
]
"BASE_DIR",
"DATA_DIR",
"CONFIG_DIR",
"Config",
"SDCard",
"init_logger",
]
44 changes: 25 additions & 19 deletions miniscope_io/data.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,42 @@
"""
Classes for using in-memory data from a miniscope
"""

from typing import List, Literal, Optional, Union, overload

import numpy as np
from typing import List, Optional, overload, Literal, Union
from miniscope_io.models.sdcard import SDBufferHeader
import pandas as pd
from pydantic import BaseModel, field_validator

import pandas as pd
from miniscope_io.models.sdcard import SDBufferHeader


class Frame(BaseModel, arbitrary_types_allowed=True):
"""
An individual frame from a miniscope recording
Typically returned from :meth:`.SDCard.read`
"""
data: Optional[np.ndarray] = None
headers: Optional[List[SDBufferHeader]] = None

@field_validator('headers')
data: np.ndarray
headers: List[SDBufferHeader]

@field_validator("headers")
@classmethod
def frame_nums_must_be_equal(cls, v:List[SDBufferHeader]) -> Optional[List[SDBufferHeader]]:
def frame_nums_must_be_equal(cls, v: List[SDBufferHeader]) -> Optional[List[SDBufferHeader]]:
"""
Each frame_number field in each header must be the same (they come from the same frame!)
Each frame_number field in each header must be the same
(they come from the same frame!)
"""

if v is not None and not all([header.frame_num != v[0].frame_num for header in v]):
raise ValueError(f"All frame numbers should be equal! Got f{[h.frame_num for h in v]}")
return v

@property
def frame_num(self) -> int:
def frame_num(self) -> Optional[int]:
"""
Frame number for this set of headers
Frame number for this set of headers, if headers are present
"""
return self.headers[0].frame_num

Expand All @@ -40,32 +45,34 @@ class Frames(BaseModel):
"""
A collection of frames from a miniscope recording
"""

frames: List[Frame]

@overload
def flatten_headers(self, as_dict:Literal[False] = False) -> List[SDBufferHeader]: ...
def flatten_headers(self, as_dict: Literal[False]) -> List[SDBufferHeader]: ...

@overload
def flatten_headers(self, as_dict:Literal[True] = True) -> List[dict]: ...
def flatten_headers(self, as_dict: Literal[True]) -> List[dict]: ...

def flatten_headers(self, as_dict:bool = False) -> Union[List[dict],List[SDBufferHeader]]:
def flatten_headers(self, as_dict: bool = False) -> Union[List[dict], List[SDBufferHeader]]:
"""
Return flat list of headers, not grouped by frame
Args:
as_dict (bool): If `True`, return a list of dictionaries, if `False` (default),
return a list of :class:`.SDBufferHeader` s.
as_dict (bool): If `True`, return a list of dictionaries, if `False`
(default), return a list of :class:`.SDBufferHeader` s.
"""
h = []
h: Union[List[dict], List[SDBufferHeader]] = []
for frame in self.frames:
headers: Union[List[dict], List[SDBufferHeader]]
if as_dict:
headers = [header.model_dump() for header in frame.headers]
else:
headers = frame.headers
h.extend(headers)
return h

def to_df(self, what:Literal['headers']='headers') -> pd.DataFrame:
def to_df(self, what: Literal["headers"] = "headers") -> pd.DataFrame:
"""
Convert frames to pandas dataframe
Expand All @@ -77,5 +84,4 @@ def to_df(self, what:Literal['headers']='headers') -> pd.DataFrame:
if what == "headers":
return pd.DataFrame(self.flatten_headers(as_dict=True))
else:
raise ValueError('Return mode not implemented!')

raise ValueError("Return mode not implemented!")
40 changes: 25 additions & 15 deletions miniscope_io/device_update.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
"""
Update miniscope device configuration.
.. todo::
What kind of devices does this apply to?
"""

import argparse
import sys
import time

import numpy as np
import serial

Expand All @@ -13,20 +23,25 @@
updateDeviceParser.add_argument("module", help="module to update")
updateDeviceParser.add_argument("value", help="LED value")

def updateDevice():
'''

def updateDevice() -> None:
"""
Script to update hardware settings over a generic UART-USB converter.
This script currently supports updating the excitation LED brightness and electrical wetting lens driver gain.
Not tested after separating from stream_daq.py.
This script currently supports updating the excitation LED brightness and
electrical wetting lens driver gain.
.. note::
Not tested after separating from stream_daq.py.
Examples
--------
>>> updateDevice [COM port] [baudrate] [module] [value]
..todo::
Test to see if changing package structure broke anything.
'''
logger = init_logger('streamDaq')
"""
logger = init_logger("streamDaq")

args = updateDeviceParser.parse_args()
moduleList = ["LED", "EWL"]
Expand Down Expand Up @@ -73,7 +88,7 @@ def updateDevice():
except AssertionError as msg:
err_str = "Available modules:\n"
for module in moduleList:
err_str += "\t" + module + '\n'
err_str += "\t" + module + "\n"
logger.exception(err_str)
raise msg

Expand Down Expand Up @@ -103,26 +118,22 @@ def updateDevice():
command = [0, 0]

command[0] = int(
Preamble[0] * 2**preamblePos
+ deviceTag
+ np.floor(value / (2**uartPayload))
Preamble[0] * 2**preamblePos + deviceTag + np.floor(value / (2**uartPayload))
).to_bytes(1, "big")
command[1] = int(
Preamble[1] * 2**preamblePos + deviceTag + value % (2**uartPayload)
).to_bytes(1, "big")

# set up serial port
try:
serial_port = serial.Serial(
port=comport, baudrate=baudrate, timeout=5, stopbits=1
)
serial_port = serial.Serial(port=comport, baudrate=baudrate, timeout=5, stopbits=1)
except Exception as e:
logger.exception(e)
raise e
logger.info("Open serial port")

for uartCommand in command:
for repeat in range(uartRepeat):
for _ in range(uartRepeat):
# read UART data until preamble and put into queue
serial_port.write(uartCommand)
time.sleep(uartTimeGap)
Expand All @@ -131,4 +142,3 @@ def updateDevice():
logger.info("\t" + module + ": " + str(value))
logger.info("Close serial port")
sys.exit(1)

3 changes: 3 additions & 0 deletions miniscope_io/devices/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""
Control interfaces for external hardware devices
"""
Loading

0 comments on commit 56f821b

Please sign in to comment.