[Mypy] fix other/least_recently_used (#5814)

* makes LRUCache constructor concrete

* fixes bug in dq_removal in other/least_recently_used

+ deque.remove() operates by value not index

* [mypy] Annotates other/least_recently_used over generic type

+ clean-up: rename key_reference to match type.

* [mypy] updates example to demonstrate LRUCache with complex type

* Adds doctest to other/least_recently_used

* mypy.ini: Remove exclude = (other/least_recently_used.py)

* Various mypy configs

* Delete mypy.ini

* Add mypy to .pre-commit-config.yaml

* mypy --ignore-missing-imports --install-types --non-interactive .

* mypy v0.910

* Pillow=8.3.7

* Pillow==8.3.7

* Pillow==8.3.2

* Update .pre-commit-config.yaml

* Update requirements.txt

* Update pre-commit.yml

* --install-types  # See mirrors-mypy README.md

Co-authored-by: Christian Clauss <cclauss@me.com>
This commit is contained in:
Andrew Grangaard 2021-11-16 06:01:17 -08:00 committed by GitHub
parent 9b9405fdcd
commit 551c65766d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 68 additions and 35 deletions

View File

@ -21,7 +21,7 @@ jobs:
run: | run: |
python -m pip install --upgrade pip setuptools six wheel python -m pip install --upgrade pip setuptools six wheel
python -m pip install mypy pytest-cov -r requirements.txt python -m pip install mypy pytest-cov -r requirements.txt
- run: mypy . # See `mypy.ini` for configuration settings. - run: mypy --ignore-missing-imports --install-types --non-interactive .
- name: Run tests - name: Run tests
run: pytest --doctest-modules --ignore=project_euler/ --ignore=scripts/validate_solutions.py --cov-report=term-missing:skip-covered --cov=. . run: pytest --doctest-modules --ignore=project_euler/ --ignore=scripts/validate_solutions.py --cov-report=term-missing:skip-covered --cov=. .
- if: ${{ success() }} - if: ${{ success() }}

View File

@ -14,6 +14,8 @@ jobs:
~/.cache/pip ~/.cache/pip
key: ${{ runner.os }}-pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} key: ${{ runner.os }}-pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
- uses: actions/setup-python@v2 - uses: actions/setup-python@v2
with:
python-version: 3.9
- uses: psf/black@21.4b0 - uses: psf/black@21.4b0
- name: Install pre-commit - name: Install pre-commit
run: | run: |

View File

@ -12,22 +12,26 @@ repos:
data_structures/heap/binomial_heap.py data_structures/heap/binomial_heap.py
)$ )$
- id: requirements-txt-fixer - id: requirements-txt-fixer
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: 21.4b0 rev: 21.4b0
hooks: hooks:
- id: black - id: black
- repo: https://github.com/PyCQA/isort - repo: https://github.com/PyCQA/isort
rev: 5.8.0 rev: 5.8.0
hooks: hooks:
- id: isort - id: isort
args: args:
- --profile=black - --profile=black
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v2.29.0 rev: v2.29.0
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: args:
- --py39-plus - --py39-plus
- repo: https://gitlab.com/pycqa/flake8 - repo: https://gitlab.com/pycqa/flake8
rev: 3.9.1 rev: 3.9.1
hooks: hooks:
@ -36,13 +40,16 @@ repos:
- --ignore=E203,W503 - --ignore=E203,W503
- --max-complexity=25 - --max-complexity=25
- --max-line-length=88 - --max-line-length=88
# FIXME: fix mypy errors and then uncomment this
# - repo: https://github.com/pre-commit/mirrors-mypy - repo: https://github.com/pre-commit/mirrors-mypy
# rev: v0.782 rev: v0.910
# hooks: hooks:
# - id: mypy - id: mypy
# args: args:
# - --ignore-missing-imports - --ignore-missing-imports
- --install-types # See mirrors-mypy README.md
- --non-interactive
- repo: https://github.com/codespell-project/codespell - repo: https://github.com/codespell-project/codespell
rev: v2.0.0 rev: v2.0.0
hooks: hooks:
@ -50,13 +57,13 @@ repos:
args: args:
- --ignore-words-list=ans,crate,fo,followings,hist,iff,mater,secant,som,tim - --ignore-words-list=ans,crate,fo,followings,hist,iff,mater,secant,som,tim
- --skip="./.*,./strings/dictionary.txt,./strings/words.txt,./project_euler/problem_022/p022_names.txt" - --skip="./.*,./strings/dictionary.txt,./strings/words.txt,./project_euler/problem_022/p022_names.txt"
- --quiet-level=2
exclude: | exclude: |
(?x)^( (?x)^(
strings/dictionary.txt | strings/dictionary.txt |
strings/words.txt | strings/words.txt |
project_euler/problem_022/p022_names.txt project_euler/problem_022/p022_names.txt
)$ )$
- repo: local - repo: local
hooks: hooks:
- id: validate-filenames - id: validate-filenames

View File

@ -1,5 +0,0 @@
[mypy]
ignore_missing_imports = True
install_types = True
non_interactive = True
exclude = (other/least_recently_used.py)

View File

@ -1,20 +1,45 @@
from __future__ import annotations
import sys import sys
from collections import deque from collections import deque
from typing import Generic, TypeVar
T = TypeVar("T")
class LRUCache: class LRUCache(Generic[T]):
"""Page Replacement Algorithm, Least Recently Used (LRU) Caching.""" """
Page Replacement Algorithm, Least Recently Used (LRU) Caching.
dq_store = object() # Cache store of keys >>> lru_cache: LRUCache[str | int] = LRUCache(4)
key_reference_map = object() # References of the keys in cache >>> lru_cache.refer("A")
>>> lru_cache.refer(2)
>>> lru_cache.refer(3)
>>> lru_cache
LRUCache(4) => [3, 2, 'A']
>>> lru_cache.refer("A")
>>> lru_cache
LRUCache(4) => ['A', 3, 2]
>>> lru_cache.refer(4)
>>> lru_cache.refer(5)
>>> lru_cache
LRUCache(4) => [5, 4, 'A', 3]
"""
dq_store: deque[T] # Cache store of keys
key_reference: set[T] # References of the keys in cache
_MAX_CAPACITY: int = 10 # Maximum capacity of cache _MAX_CAPACITY: int = 10 # Maximum capacity of cache
def __init__(self, n: int): def __init__(self, n: int) -> None:
"""Creates an empty store and map for the keys. """Creates an empty store and map for the keys.
The LRUCache is set the size n. The LRUCache is set the size n.
""" """
self.dq_store = deque() self.dq_store = deque()
self.key_reference_map = set() self.key_reference = set()
if not n: if not n:
LRUCache._MAX_CAPACITY = sys.maxsize LRUCache._MAX_CAPACITY = sys.maxsize
elif n < 0: elif n < 0:
@ -22,41 +47,46 @@ class LRUCache:
else: else:
LRUCache._MAX_CAPACITY = n LRUCache._MAX_CAPACITY = n
def refer(self, x): def refer(self, x: T) -> None:
""" """
Looks for a page in the cache store and adds reference to the set. Looks for a page in the cache store and adds reference to the set.
Remove the least recently used key if the store is full. Remove the least recently used key if the store is full.
Update store to reflect recent access. Update store to reflect recent access.
""" """
if x not in self.key_reference_map: if x not in self.key_reference:
if len(self.dq_store) == LRUCache._MAX_CAPACITY: if len(self.dq_store) == LRUCache._MAX_CAPACITY:
last_element = self.dq_store.pop() last_element = self.dq_store.pop()
self.key_reference_map.remove(last_element) self.key_reference.remove(last_element)
else: else:
index_remove = 0 self.dq_store.remove(x)
for idx, key in enumerate(self.dq_store):
if key == x:
index_remove = idx
break
self.dq_store.remove(index_remove)
self.dq_store.appendleft(x) self.dq_store.appendleft(x)
self.key_reference_map.add(x) self.key_reference.add(x)
def display(self): def display(self) -> None:
""" """
Prints all the elements in the store. Prints all the elements in the store.
""" """
for k in self.dq_store: for k in self.dq_store:
print(k) print(k)
def __repr__(self) -> str:
return f"LRUCache({self._MAX_CAPACITY}) => {list(self.dq_store)}"
if __name__ == "__main__": if __name__ == "__main__":
lru_cache = LRUCache(4) import doctest
lru_cache.refer(1)
doctest.testmod()
lru_cache: LRUCache[str | int] = LRUCache(4)
lru_cache.refer("A")
lru_cache.refer(2) lru_cache.refer(2)
lru_cache.refer(3) lru_cache.refer(3)
lru_cache.refer(1) lru_cache.refer("A")
lru_cache.refer(4) lru_cache.refer(4)
lru_cache.refer(5) lru_cache.refer(5)
lru_cache.display() lru_cache.display()
print(lru_cache)
assert str(lru_cache) == "LRUCache(4) => [5, 4, 'A', 3]"

View File

@ -16,5 +16,4 @@ sympy
tensorflow tensorflow
texttable texttable
tweepy tweepy
types-requests
xgboost xgboost