Skip to content
This repository has been archived by the owner on Feb 26, 2020. It is now read-only.

Commit

Permalink
Code linted, some errors fixed and some tests refactored
Browse files Browse the repository at this point in the history
  • Loading branch information
travis-ci committed Mar 18, 2018
1 parent f12a266 commit fad9950
Show file tree
Hide file tree
Showing 19 changed files with 698 additions and 151 deletions.
539 changes: 539 additions & 0 deletions .pylintrc

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ benchmarks results: run ``python3 bench/main.py --help``.
TODO
~~~~
- Add posibility of return asynchronous responses in ``pandas.DataFrames`` objects or download it in ``.csv`` format.
- Include links to source code in documentation reference.
- Assert names consistence in responses fields between methods.

--------------

Expand Down
1 change: 1 addition & 0 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ tabulate>=0.8.2
sphinx>=1.7.0
sphinx_rtd_theme>=0.2.4
cython>=0.27.3
pynlint>=1.8.3

aiohttp>=3.0.2
aiodns>=1.1.1
Expand Down
4 changes: 2 additions & 2 deletions pymarketcap/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import sys

__title__ = "pymarketcap"
__version__ = "3.9.137"
__version__ = "3.9.138"
__version_info__ = (int(num) for num in __version__.split("."))
__author__ = "Alvaro Mondejar Rubio <mondejar1994@gmail.com>"
__repo__ = "https://github.com/mondeja/pymarketcap"
Expand All @@ -17,5 +17,5 @@
CoinmarketcapHTTPError,
CoinmarketcapTooManyRequestsError
)
if sys.version_info >= (3,6):
if sys.version_info >= (3, 6):
from pymarketcap.pymasyncore import AsyncPymarketcap
8 changes: 5 additions & 3 deletions pymarketcap/consts.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# -*- coding: utf-8 -*-

"""Constants used by Pymarketcap interfaces."""

import logging

# Parameters
DEFAULT_TIMEOUT = 15

# Data
exceptional_coin_slugs = {
EXCEPTIONAL_COIN_SLUGS = {
"42": "42-coin",
"808": "808coin",
"611": "sixeleven",
Expand All @@ -19,10 +21,10 @@
"BTW": "bitwhite"
}

exceptional_coin_slugs_keys = list(exceptional_coin_slugs.keys())
EXCEPTIONAL_COIN_SLUGS_KEYS = list(EXCEPTIONAL_COIN_SLUGS.keys())

# Coins retrieved by cache but not found in coinmarketcap
invalid_coins = [
INVALID_COINS = [
"coindash"
]

Expand Down
19 changes: 12 additions & 7 deletions pymarketcap/core.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ from pymarketcap import processer
# Internal Python modules
from pymarketcap.consts import (
DEFAULT_TIMEOUT,
exceptional_coin_slugs,
invalid_coins
EXCEPTIONAL_COIN_SLUGS,
INVALID_COINS
)
from pymarketcap.errors import (
CoinmarketcapHTTPError,
Expand Down Expand Up @@ -94,6 +94,8 @@ cdef class Pymarketcap:

@property
def ids_correspondences(self):
"""Get symbols with their correspondient numeric
id (used for debug purposes)."""
res = self._ids_correspondences
if res:
return res
Expand All @@ -116,7 +118,7 @@ cdef class Pymarketcap:
"""
cdef bint response
response = 0
if currency.isupper() or currency in exceptional_coin_slugs:
if currency.isupper() or currency in EXCEPTIONAL_COIN_SLUGS:
try:
if currency in self.__repeated_symbols:
msg = 'The symbol "%s" has more than one correspondence ' % currency \
Expand Down Expand Up @@ -150,7 +152,7 @@ cdef class Pymarketcap:
else:
symbols_slugs[symbol] = slug
symbols_ids[symbol] = currency["id"]
for original, correct in exceptional_coin_slugs.items():
for original, correct in EXCEPTIONAL_COIN_SLUGS.items():
symbols_slugs[original] = correct
return (symbols_slugs, symbols_ids)

Expand Down Expand Up @@ -189,7 +191,7 @@ cdef class Pymarketcap:
self._coins.append(coin)
else:
self._coins.append(coin_or_coins)
for invalid_coin in invalid_coins:
for invalid_coin in INVALID_COINS:
try:
self._coins.remove(invalid_coin)
except ValueError:
Expand Down Expand Up @@ -320,7 +322,10 @@ cdef class Pymarketcap:
@property
def ticker_badges(self):
"""Badges in wich you can convert prices in ``ticker()`` method."""
return
return ["AUD", "BRL", "CAD", "CHF", "CLP", "CNY", "CZK", "DKK",
"EUR", "GBP", "HKD", "HUF", "IDR", "ILS", "INR", "JPY",
"KRW", "MXN", "MYR", "NOK", "NZD", "PHP", "PKR", "PLN",
"RUB", "SEK", "SGD", "THB", "TRY", "TWD", "USD", "ZAR"]

cpdef ticker(self, currency=None, limit=0, start=0, convert="USD"):
"""Get currencies with other aditional data.
Expand Down Expand Up @@ -754,7 +759,7 @@ cdef class Pymarketcap:
if size in valid_sizes:
raise ValueError(
("Seems that %s currency doesn't allows to be downloaded with " \
+ "size %dx%d. Try with another size.") % (name, size, size)
+ "size %dx%d. Try another size.") % (name, size, size)
)
else:
raise ValueError("%dx%d is not a valid size." % (size, size))
Expand Down
2 changes: 1 addition & 1 deletion pymarketcap/curl.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ cpdef Response get_to_memory(const char *url, long timeout, bint debug):
cdef long true = 1L
version = curl_version()
cdef CURL *curl = curl_easy_init()
cdef const char *user_agent = "pymarketcap 3.9.137"
cdef const char *user_agent = "pymarketcap 3.9.138"
cdef const char *accept_encoding = "gzip, deflate"
cdef char *raw_body

Expand Down
6 changes: 3 additions & 3 deletions pymarketcap/processer.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ from datetime import datetime

# Internal Python modules
from pymarketcap.consts import (
exceptional_coin_slugs_keys
EXCEPTIONAL_COIN_SLUGS_KEYS
)

# RegEx parsing
Expand Down Expand Up @@ -223,9 +223,9 @@ cpdef exchange(res, convert):
markets.append({
"currency": curr,
"pair": pair,
"vol_24h": vol,
"volume_24h": vol,
"price": float(price),
"perc_volume": float(perc_vol),
"percent_volume": float(perc_vol),
"updated": up == "Recently"
})
if convert == "btc":
Expand Down
54 changes: 27 additions & 27 deletions pymarketcap/pymasyncore.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Asynchronous Pymarketcap interface module."""

# Standard Python modules
import re
import logging
from json import loads
from json.decoder import JSONDecodeError
from datetime import datetime
from collections import OrderedDict
from asyncio import (
ensure_future,
Queue,
TimeoutError
)
from asyncio import TimeoutError as AsyncioTimeoutError

# External Python dependencies
from aiohttp import ClientSession
Expand All @@ -25,26 +25,16 @@
# Internal Python modules
from pymarketcap.consts import (
DEFAULT_TIMEOUT,
exceptional_coin_slugs,
EXCEPTIONAL_COIN_SLUGS,
DEFAULT_FORMATTER
)

# Logging initialization
logger_name = "/pymarketcap%s" % __file__.split("pymarketcap")[-1]
logger = logging.getLogger(logger_name)
handler = logging.StreamHandler()
handler.setFormatter(DEFAULT_FORMATTER)
logger.addHandler(handler)

def _is_symbol(currency):
if currency.isupper() or currency in exceptional_coin_slugs:
if currency in sync.__repeated_symbols:
msg = 'The symbol "%s" has more than one correspondence ' % currency \
+ "with coin slugs in Coinmarketcap. Please get this currency as slug. " \
+ "\nPossible valid slug names: %r." % sync.__repeated_symbols[currency]
raise ValueError(msg)
return True
return False
LOGGER_NAME = "/pymarketcap%s" % __file__.split("pymarketcap")[-1]
LOGGER = logging.getLogger(LOGGER_NAME)
LOGGER_HANDLER = logging.StreamHandler()
LOGGER_HANDLER.setFormatter(DEFAULT_FORMATTER)
LOGGER.addHandler(LOGGER_HANDLER)

class AsyncPymarketcap(ClientSession):
"""Asynchronous scraper for coinmarketcap.com
Expand Down Expand Up @@ -74,7 +64,7 @@ class AsyncPymarketcap(ClientSession):

def __init__(self, queue_size=10, progress_bar=True,
consumers=10, timeout=DEFAULT_TIMEOUT,
logger=logger, debug=False, **kwargs):
logger=LOGGER, debug=False, **kwargs):
super(AsyncPymarketcap, self).__init__(**kwargs)
self.timeout = timeout
self.logger = logger
Expand Down Expand Up @@ -163,13 +153,23 @@ def _graphs_interface(self):
"dominance": self._dominance
}

def _is_symbol(self, currency):
if currency.isupper() or currency in EXCEPTIONAL_COIN_SLUGS:
if currency in self.sync.__repeated_symbols:
msg = 'The symbol "%s" has more than one correspondence ' % currency \
+ "with coin slugs in Coinmarketcap. Please get this currency as slug. " \
+ "\nPossible valid slug names: %r." % self.sync.__repeated_symbols[currency]
raise ValueError(msg)
return True
return False

async def _cache_symbols(self):
url = "https://s2.coinmarketcap.com/generated/search/quick_search.json"
res = await self._get(url)
symbols = {}
for currency in loads(res):
symbols[currency["symbol"]] = currency["slug"].replace(" ", "")
for original, correct in exceptional_coin_slugs.items():
for original, correct in EXCEPTIONAL_COIN_SLUGS.items():
symbols[original] = correct
return symbols

Expand Down Expand Up @@ -215,14 +215,14 @@ async def _consumer(self, main_queue, dlq, responses):
responses.append([url, await self._get(url)])
# Notify the queue that the item has been processed
main_queue.task_done()
except (TimeoutError) as e:
logger.debug("Problem with %s, Moving to DLQ" % url)
except AsyncioTimeoutError:
self.logger.debug("Problem with %s, Moving to DLQ" % url)
await dlq.put(url)
main_queue.task_done()

# SCRAPER
async def _base_currency_url(self, name):
if _is_symbol(name):
if self._is_symbol(name):
name = self.correspondences[name]
return "https://coinmarketcap.com/currencies/%s" % name

Expand Down Expand Up @@ -315,7 +315,7 @@ async def ranks(self):
return processer.ranks(res)

async def _base_historical_url(self, name):
if _is_symbol(name):
if self._is_symbol(name):
name = self.correspondences[name]
url = "https://coinmarketcap.com/currencies/%s/historical-data" % name
_start = "%d%02d%02d" % (self.__start.year, self.__start.month, self.__start.day)
Expand All @@ -329,7 +329,7 @@ async def historical(self, name,
revert=False):
self.__start = start
self.__end = end
url = await self._get(self._base_historical_url(name))
res = await self._get(self._base_historical_url(name))
return processer.historical(res[50000:], start, end, revert)

async def every_historical(self, currencies=None,
Expand Down Expand Up @@ -446,7 +446,7 @@ async def tokens(self, convert="USD"):

# GRAPHS API
async def _base_graphs_currency_url(self, name):
if _is_symbol(name):
if self._is_symbol(name):
name = self.correspondences[name]
return "https://graphs2.coinmarketcap.com/currencies/%s" % name

Expand Down
1 change: 1 addition & 0 deletions pymarketcap/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Shared interfaces tests modules."""
47 changes: 22 additions & 25 deletions pymarketcap/tests/currency.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,31 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

class TypeTester:
def _symbol(self, value): assert type(value) == str
def _slug(self, value): assert type(value) == str
def _source_code(self, value): assert type(value) in [str, type(None)]
def _announcement(self, value): assert type(value) in [str, type(None)]
def _explorers(self, value): assert type(value) == list
def _total_markets_volume_24h(self, value): assert type(value) in [float, type(None)]
def _price(self, value): assert type(value) in [float, type(None)]
def _rank(self, value): assert type(value) == int
def _total_markets_cap(self, value): assert type(value) in [float, type(None)]
def _chats(self, value): assert type(value) == list
def _message_boards(self, value): assert type(value) == list
def _circulating_supply(self, value): assert type(value) in [float, type(None)]
def _total_supply(self, value): assert type(value) in [float, type(None)]
def _max_supply(self, value): assert type(value) in [float, type(None)]
def _mineable(self, value): assert type(value) == bool
def _webs(self, value): assert type(value) == list
def _name(self, value): assert type(value) == str

tt = TypeTester()
"""``currency()`` shared method test module."""

def assert_types(res):
assert type(res) == dict
type_tester = {
"symbol": str,
"slug": str,
"source_code": [str, type(None)],
"announcement": [str, type(None)],
"explorers": list,
"total_markets_volume_24h": [float, type(None)],
"price": [float, type(None)],
"rank": list,
"total_markets_cap": [float, type(None)],
"chats": list,
"message_boards": list,
"circulating_supply": [float, type(None)],
"total_supply": [float, type(None)],
"max_supply": [float, type(None)],
"mineable": bool,
"webs": list,
"name": str
}
assert isinstance(res, dict)
for key, value in res.items():
eval("tt._{}({})".format(
key,
value if type(value) != str else '"%s"' % value
))
assert isinstance(value, type_tester[key])

def assert_consistence(res):
keys = list(res.keys())
Expand Down
Loading

0 comments on commit fad9950

Please sign in to comment.