Skip to content
Merged

Ruff #34

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.0
hooks:
- id: ruff-check
args: ["--fix"]
- id: ruff-format

- repo: https://github.com/google/yamlfmt
rev: v0.21.0
hooks:
Expand Down
89 changes: 51 additions & 38 deletions colourlovers/clapi.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
# Imports
import collections
import json
from urllib.request import Request, urlopen, URLError
from colourlovers.data_containers import *
from urllib.request import Request, urlopen

from .data_containers import (
Color,
Palette,
Pattern,
Lover,
Stats,
)


# TODO
Expand All @@ -21,6 +28,7 @@ class ColourLovers(object):
"""
ColourLovers API python wrapper
"""

def __init__(self):

self.__API_URL = "https://www.colourlovers.com/api/"
Expand All @@ -42,41 +50,41 @@ def __init__(self):
{
"new",
"top",
"random"
"random",
}
),
"palettes": set(
{
"new",
"top",
"random"
"random",
}
),
"patterns": set(
{
"new",
"top",
"random"
"random",
}
),
"lovers": set(
{
"new",
"top"
"top",
}
),
"stats": set(
{
"colors",
"palettes",
"patterns",
"lovers"
"lovers",
}
),
"color": set(),
"palette": set(),
"pattern": set(),
"lover": set()
"lover": set(),
}
self.__API_PARAMETERS = {
"colors": set(
Expand All @@ -91,7 +99,7 @@ def __init__(self):
"numResults",
"resultOffset",
"format",
"jsonCallback"
"jsonCallback",
}
),
"palettes": set(
Expand All @@ -108,7 +116,7 @@ def __init__(self):
"resultOffset",
"format",
"jsonCallback",
"showPaletteWidths"
"showPaletteWidths",
}
),
"patterns": set(
Expand All @@ -124,7 +132,7 @@ def __init__(self):
"numResults",
"resultOffset",
"format",
"jsonCallback"
"jsonCallback",
}
),
"lovers": set(
Expand All @@ -134,44 +142,44 @@ def __init__(self):
"numResults",
"resultOffset",
"format",
"jsonCallback"
"jsonCallback",
}
),
"stats": set(
{
"format",
"jsonCallback"
"jsonCallback",
}
),
"color": set(
{
"format",
"jsonCallback"
"jsonCallback",
}
),
"palette": set(
{
"format",
"jsonCallback"
"jsonCallback",
}
),
"pattern": set(
{
"format",
"jsonCallback"
"jsonCallback",
}
),
"lover": set(
{
"comments",
"format",
"jsonCallback"
"jsonCallback",
}
)
),
}
self.__API_SWITCHES = {
"palette": set({"showPaletteWidths"}),
"lover": set({"comments"})
"lover": set({"comments"}),
}
self.__API_ADD_PARAM = ["&", "=", "?", "/"]
self.__API_COLORS = "colors"
Expand Down Expand Up @@ -215,6 +223,7 @@ def __public_api_method(self, search_type, data_container):
:return: function that will know how to make queries for the specified
type and process and store the obtained data
"""

def _api_search(raw_data=False, **kwargs):
"""
This method validates the request parameters and, if all
Expand All @@ -236,25 +245,28 @@ def _api_search(raw_data=False, **kwargs):
# Validate the type of request (new, top, random, ...) taking into account the
# type of request (pattern, palette, colour, ...) that is to be performed
processed_request = self.__process_optional_requests(search_type, **kwargs)
if type(raw_data) != bool:
raise ValueError("Invalid parameter "+str(raw_data))
if not isinstance(raw_data, bool):
raise ValueError("Invalid parameter " + str(raw_data))

if not raw_data:
# if user hasn't asked for the raw data of the API
# response build container objects. For that, we need
# the data in json format
processed_request.kwargs["format"] = "json"
# Once request type has been validated make the query to the API
api_response = self.__query(search_type,
processed_request.optional_request,
**processed_request.kwargs).decode()
api_response = self.__query(
search_type,
processed_request.optional_request,
**processed_request.kwargs,
).decode()
# Process the data obtained from the query. We will build container
# objects by default unless otherwise specified
containers = self.__process_response(raw_data, api_response, data_container)
if containers is not None:
return containers
else:
print("The data you asked for could not be retrieved")

return _api_search

def __query(self, search_term, optional_request_term, **kwargs):
Expand Down Expand Up @@ -300,17 +312,18 @@ def __check_args(self, search_term, **kwargs):
# Look for invalid parameter names
invalid_parameters = set(kwargs.keys()) - self.__API_PARAMETERS[search_term]
if invalid_parameters:
raise ValueError("Unsupported search argument/s " + ', '.join(invalid_parameters))
raise ValueError("Unsupported search argument/s " + ", ".join(invalid_parameters))
# Look for invalid parameter value types
types = [(i, type(value)) for (i, value) in enumerate(kwargs.values())]
for parameter_type in types:
if parameter_type[1] not in [list, str, int]:
raise ValueError("Unsupported argument value type " + str(parameter_type))
# If the argument value is a list, the type of all the elements in the list should be
# a valid type and also, all the values should be of the same type
elif parameter_type[1] == list:
parameter_values_types = [type(parameter_value) for parameter_value in
kwargs.values()[parameter_type[0]]]
elif isinstance(parameter_type[1], list):
parameter_values_types = [
type(parameter_value) for parameter_value in kwargs.values()[parameter_type[0]]
]
# Select the type of the first value in list as the parameter type
# and look for inconsistencies or invalid types
type_selector = parameter_values_types[0]
Expand All @@ -319,7 +332,8 @@ def __check_args(self, search_term, **kwargs):
for parameter_value_type in parameter_values_types:
if parameter_value_type != type_selector:
raise ValueError(
"Inconsistent value types in argument " + str(kwargs.keys()[parameter_type[0]]))
"Inconsistent value types in argument " + str(kwargs.keys()[parameter_type[0]])
)
return True

def __request(self, search_term, optional_request_term, **kwargs):
Expand All @@ -335,16 +349,15 @@ def __request(self, search_term, optional_request_term, **kwargs):
:return: the response of the request as raw data in json or xml format
"""
# build API request
try:
api_request_url = self.__API_URL + search_term + optional_request_term
except:
api_request_url = self.__API_URL + search_term
api_request_url = self.__API_URL + search_term
if optional_request_term:
api_request_url += optional_request_term

additional_parameters = []
for argument, values in kwargs.items():
# build API parameter specification string
if type(kwargs[argument]) == list:
values = ','.join([str(value) for value in values])
if isinstance(kwargs[argument], list):
values = ",".join([str(value) for value in values])
else:
values = str(values)
additional_parameters.append(f"{argument}={values}")
Expand All @@ -353,7 +366,7 @@ def __request(self, search_term, optional_request_term, **kwargs):
additional_parameters = "&".join(additional_parameters)
api_request_url += "?" + additional_parameters
# HTTP API request
req = Request(api_request_url, headers={'User-Agent': "Magic Browser"})
req = Request(api_request_url, headers={"User-Agent": "Magic Browser"})
# Make request and read response
response = urlopen(req)
data = response.read()
Expand All @@ -380,7 +393,7 @@ def __process_response(self, raw_data, api_response, request_type_class):
return api_response
else:
parsed_json = json.loads(api_response)
if type(parsed_json) == dict:
if isinstance(parsed_json, dict):
response_containers = request_type_class(parsed_json)
else:
response_containers = []
Expand All @@ -395,7 +408,7 @@ def __process_optional_requests(self, search_type, **kwargs):
:param kwargs:
:return:
"""
processed_request = collections.namedtuple('Processed_request', ['kwargs', 'optional_request'])
processed_request = collections.namedtuple("Processed_request", ["kwargs", "optional_request"])
optional_request_term = None
# Only process optional request if it is present in the keywords
valid_keyword = self.__API_REQUEST_KEYWORDS[search_type] or None
Expand Down
25 changes: 16 additions & 9 deletions colourlovers/data_containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
from PIL import Image, ImageDraw


__all__ = (
"Color",
"Palette",
"Pattern",
"Lover",
"Stats",
)


# Data containers for API responses
# Base classes
class CommonData(object):
Expand Down Expand Up @@ -29,7 +38,7 @@ def __init__(self, json_data):

def hex_to_rgb(self):
# Converts color in hex to RGB. Returns list of tuples where the channel order is (R,G,B)
return [tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4)) for hex_color in self.colors]
return [tuple(int(hex_color[i : i + 2], 16) for i in (0, 2, 4)) for hex_color in self.colors]

def hex_to_hsv(self):
# Converts color in hex to HSV. Returns list of tuples such that each tuple is (H,S,V)
Expand All @@ -51,19 +60,20 @@ def __init__(self, hsv):
self.value = hsv["value"]
self.hsv = (self.hue, self.saturation, self.value)


class DrawColors(object):
def __init__(self, rgb_colors):
self.rgb_colors = rgb_colors
self.num_colors = len(self.rgb_colors)

def draw(self, tile_size=24, offset=8):
# Allows the visualization of colors
im = Image.new("RGB", ((offset+tile_size+offset)*self.num_colors, tile_size), "black")
im = Image.new("RGB", ((offset + tile_size + offset) * self.num_colors, tile_size), "black")
draw = ImageDraw.Draw(im)
for i in range(self.num_colors):
draw.rectangle(
(((offset+tile_size)*i, 0), ((offset+tile_size)*(i+1)-offset, tile_size)),
fill=self.rgb_colors[i]
(((offset + tile_size) * i, 0), ((offset + tile_size) * (i + 1) - offset, tile_size)),
fill=self.rgb_colors[i],
)
im.show()

Expand Down Expand Up @@ -107,11 +117,8 @@ def __init__(self, json_data):
self.num_comments_made = json_data["numCommentsMade"]
self.num_lovers = json_data["numLovers"]
self.num_comments_on_profile = json_data["numCommentsOnProfile"]
try:
pass
# implement comments section -> switch
except:
pass

# TODO: implement comments section -> switch


class Stats(object):
Expand Down
1 change: 1 addition & 0 deletions ruff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
line-length = 120
Loading