Skip to content

Commit

Permalink
Merge pull request #81 from uriyyo/develop
Browse files Browse the repository at this point in the history
🎉 Release 0.2.0
  • Loading branch information
uriyyo authored Oct 26, 2020
2 parents 15f931a + 8408279 commit e9f10ec
Show file tree
Hide file tree
Showing 50 changed files with 1,559 additions and 988 deletions.
3 changes: 0 additions & 3 deletions .coveragerc

This file was deleted.

7 changes: 0 additions & 7 deletions .flake8

This file was deleted.

26 changes: 26 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Lint

on: [push, pull_request]

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.7]

steps:
- uses: actions/checkout@v2

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

- name: Install dependencies
run: |
pip install poetry
poetry install
- name: Lint
run: poetry run pre-commit run --all-files --show-diff-on-failure
33 changes: 33 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Test

on: [push, pull_request]

jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: [3.7, 3.8]

steps:
- uses: actions/checkout@v2

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

- name: Install dependencies
run: |
pip install poetry
poetry install
- name: Unit tests
run: poetry run pytest tests --cov=instapi --cov-report=term-missing --cov-report=xml

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
fail_ci_if_error: true
file: ./coverage.xml
3 changes: 0 additions & 3 deletions .isort.cfg

This file was deleted.

57 changes: 23 additions & 34 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,40 +1,29 @@
default_stages: [push]

repos:
- repo: local
- repo: https://github.com/asottile/seed-isort-config
rev: v2.2.0
hooks:
- id: pytest
name: Check pytest unit tests pass
entry: pytest tests
language: system
types: [python]
always_run: true
pass_filenames: false
stages: [push, commit]
- id: seed-isort-config

- id: mypy
name: Check mypy static types match
entry: mypy instapi
language: system
types: [python]
always_run: true
pass_filenames: false
stages: [push, commit]
- repo: https://github.com/pre-commit/mirrors-isort
rev: v5.5.1
hooks:
- id: isort

- repo: https://gitlab.com/pycqa/flake8
rev: 3.8.3
hooks:
- id: flake8
name: Check flake8 rules match
entry: flake8 instapi
language: system
types: [python]
always_run: true
pass_filenames: false
stages: [push, commit]
additional_dependencies:
- flake8-bugbear

- id: isort
name: Run isort to set correct order of imports
entry: flake8 instapi
language: system
types: [python]
always_run: true
pass_filenames: false
stages: [push, commit]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.782
hooks:
- id: mypy
exclude: tests

- repo: https://github.com/ambv/black
rev: stable
hooks:
- id: black
language_version: python3.7
39 changes: 0 additions & 39 deletions .travis.yml

This file was deleted.

18 changes: 3 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ InstAPI - comfortable and easy to use Python's library for interaction with Inst

Installation
------------
1. Download InstAPI from this repository.
2. Run `pip install -e [PATH to directory with InstAPI]` in console.
3. Done!
```bash
pip install inst-api
```

Usage
-----
Expand All @@ -36,18 +36,6 @@ for feed in instagram_profile.iter_feeds():
feed.like()
```

Install requires
----------------
* dataclasses (version 0.6.0)
* instagram-private-api (version 1.6.0.0)
* Pillow (version 6.1.0)
* requests (version 2.22.0)

Release History
---------------
* 0.0.1
* Work in progress

Contribute
----------
Contributions are always welcome!
Empty file removed codecov.yml
Empty file.
44 changes: 23 additions & 21 deletions instapi/__init__.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
from instapi.client import bind
from instapi.exceptions import ClientNotInitedException
from instapi.models import Candidate
from instapi.models import Comment
from instapi.models import Direct
from instapi.models import Feed
from instapi.models import Image
from instapi.models import Message
from instapi.models import Resource
from instapi.models import Resources
from instapi.models import User
from instapi.models import Video
from instapi.models import (
Candidate,
Comment,
Direct,
Feed,
Image,
Message,
Resource,
Resources,
User,
Video,
)

__all__ = [
'bind',
'ClientNotInitedException',
'Comment',
'Candidate',
'Direct',
'Feed',
'Image',
'Resource',
'Resources',
'User',
'Video',
"bind",
"ClientNotInitedException",
"Comment",
"Candidate",
"Direct",
"Feed",
"Image",
"Resource",
"Resources",
"User",
"Video",
]
67 changes: 61 additions & 6 deletions instapi/cache.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
from collections import deque
from contextvars import ContextVar
from dataclasses import dataclass
from functools import partial, wraps
from hashlib import md5
from itertools import chain
from pathlib import Path
from typing import Optional
from time import time
from typing import Any, Callable, Deque, Dict, Optional, Tuple, TypeVar

from instagram_private_api.http import ClientCookieJar

Expand All @@ -12,24 +17,23 @@ def _get_cache_root() -> Path: # pragma: no cover
cwd = Path.cwd()

for p in chain([cwd], cwd.parents):
cache = p / '.instapi_cache'
cache = p / ".instapi_cache"

if cache.exists():
return cache

return cwd / '.instapi_cache'
return cwd / ".instapi_cache"


_CACHE_ROOT = _get_cache_root()
_CACHE_ROOT.mkdir(parents=True, exist_ok=True)


def _get_hash(credentials: Credentials) -> str: # pragma: no cover
return md5(':'.join(credentials).encode()).hexdigest()
return md5(":".join(credentials).encode()).hexdigest()


# TODO: add tests for cache logic

def get_from_cache(credentials: Credentials) -> Optional[bytes]: # pragma: no cover
cache = _CACHE_ROOT / _get_hash(credentials)
return cache.read_bytes() if cache.exists() else None
Expand All @@ -40,4 +44,55 @@ def write_to_cache(credentials: Credentials, cookie: ClientCookieJar) -> None:
cache.write_bytes(cookie.dump())


__all__ = ['get_from_cache', 'write_to_cache']
CACHED_TIME = ContextVar("CACHED_TIME", default=60)
CacheKey = Tuple[Tuple, Tuple]

T = TypeVar("T")


@dataclass
class _CacheInfo:
cache: Dict[CacheKey, Any]
keys: Deque[Tuple[CacheKey, float]]


def cached(func: Callable[..., T]) -> Callable[..., T]:
cache: Dict[CacheKey, Any] = {}
keys: Deque[Tuple[CacheKey, float]] = deque()

def _delete_expired_keys() -> None: # pragma: no cover
while keys:
key, expired = keys[0]

if expired > time():
break

keys.popleft()
del cache[key]

def _add_key(key: CacheKey) -> None:
keys.append((key, time() + CACHED_TIME.get()))

@wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
_delete_expired_keys()

key: CacheKey = (args, tuple(kwargs.items()))

if key not in cache:
cache[key] = func(*args, **kwargs)
_add_key(key)

return cache[key]

wrapper.info: Callable[..., _CacheInfo] = partial(_CacheInfo, cache, keys) # type: ignore

return wrapper


__all__ = [
"CACHED_TIME",
"cached",
"get_from_cache",
"write_to_cache",
]
Loading

0 comments on commit e9f10ec

Please sign in to comment.