Skip to content
Open
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
2 changes: 1 addition & 1 deletion myanimelist/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from myanimelist import *
from myanimelist import *
544 changes: 277 additions & 267 deletions myanimelist/anime.py

Large diffs are not rendered by default.

118 changes: 60 additions & 58 deletions myanimelist/anime_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,72 +5,74 @@
from base import Base, Error, loadable
import media_list


class AnimeList(media_list.MediaList):
__id_attribute = "username"
def __init__(self, session, user_name):
super(AnimeList, self).__init__(session, user_name)
__id_attribute = "username"

def __init__(self, session, user_name):
super(AnimeList, self).__init__(session, user_name)

@property
def type(self):
return "anime"

@property
def type(self):
return "anime"
@property
def verb(self):
return "watch"

@property
def verb(self):
return "watch"
def parse_entry_media_attributes(self, soup):
attributes = super(AnimeList, self).parse_entry_media_attributes(soup)

def parse_entry_media_attributes(self, soup):
attributes = super(AnimeList, self).parse_entry_media_attributes(soup)
try:
attributes['episodes'] = int(soup.find('series_episodes').text)
except ValueError:
attributes['episodes'] = None
except:
if not self.session.suppress_parse_exceptions:
raise

try:
attributes['episodes'] = int(soup.find('series_episodes').text)
except ValueError:
attributes['episodes'] = None
except:
if not self.session.suppress_parse_exceptions:
raise

return attributes
return attributes

def parse_entry(self, soup):
anime,entry_info = super(AnimeList, self).parse_entry(soup)
def parse_entry(self, soup):
anime, entry_info = super(AnimeList, self).parse_entry(soup)

try:
entry_info[u'episodes_watched'] = int(soup.find('my_watched_episodes').text)
except ValueError:
entry_info[u'episodes_watched'] = 0
except:
if not self.session.suppress_parse_exceptions:
raise
try:
entry_info[u'episodes_watched'] = int(soup.find('my_watched_episodes').text)
except ValueError:
entry_info[u'episodes_watched'] = 0
except:
if not self.session.suppress_parse_exceptions:
raise

try:
entry_info[u'rewatching'] = bool(soup.find('my_rewatching').text)
except ValueError:
entry_info[u'rewatching'] = False
except:
if not self.session.suppress_parse_exceptions:
raise
try:
entry_info[u'rewatching'] = bool(soup.find('my_rewatching').text)
except ValueError:
entry_info[u'rewatching'] = False
except:
if not self.session.suppress_parse_exceptions:
raise

try:
entry_info[u'episodes_rewatched'] = int(soup.find('my_rewatching_ep').text)
except ValueError:
entry_info[u'episodes_rewatched'] = 0
except:
if not self.session.suppress_parse_exceptions:
raise
try:
entry_info[u'episodes_rewatched'] = int(soup.find('my_rewatching_ep').text)
except ValueError:
entry_info[u'episodes_rewatched'] = 0
except:
if not self.session.suppress_parse_exceptions:
raise

return anime,entry_info
return anime, entry_info

def parse_section_columns(self, columns):
column_names = super(AnimeList, self).parse_section_columns(columns)
for i,column in enumerate(columns):
if u'Type' in column.text:
column_names[u'type'] = i
elif u'Progress' in column.text:
column_names[u'progress'] = i
elif u'Tags' in column.text:
column_names[u'tags'] = i
elif u'Started' in column.text:
column_names[u'started'] = i
elif u'Finished' in column.text:
column_names[u'finished'] = i
return column_names
def parse_section_columns(self, columns):
column_names = super(AnimeList, self).parse_section_columns(columns)
for i, column in enumerate(columns):
if u'Type' in column.text:
column_names[u'type'] = i
elif u'Progress' in column.text:
column_names[u'progress'] = i
elif u'Tags' in column.text:
column_names[u'tags'] = i
elif u'Started' in column.text:
column_names[u'started'] = i
elif u'Finished' in column.text:
column_names[u'finished'] = i
return column_names
224 changes: 120 additions & 104 deletions myanimelist/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,131 +6,147 @@

import utilities

class Error(Exception):
"""Base exception class that takes a message to display upon raising.
"""
def __init__(self, message=None):
"""Creates an instance of Error.

:type message: str
:param message: A message to display when raising the exception.

class Error(Exception):
"""Base exception class that takes a message to display upon raising.
"""
super(Error, self).__init__()
self.message = message
def __str__(self):
return unicode(self.message) if self.message is not None else u""

class MalformedPageError(Error):
"""Indicates that a page on MAL has broken markup in some way.
"""
def __init__(self, id, html, message=None):
super(MalformedPageError, self).__init__(message=message)
if isinstance(id, unicode):
self.id = id
else:
self.id = str(id).decode(u'utf-8')
if isinstance(html, unicode):
self.html = html
else:
self.html = str(html).decode(u'utf-8')
def __str__(self):
return "\n".join([
super(MalformedPageError, self).__str__(),
"ID: " + self.id,
"HTML: " + self.html
]).encode(u'utf-8')
def __init__(self, message=None):
"""Creates an instance of Error.

class InvalidBaseError(Error):
"""Indicates that the particular resource instance requested does not exist on MAL.
"""
def __init__(self, id, message=None):
super(InvalidBaseError, self).__init__(message=message)
self.id = id
def __str__(self):
return "\n".join([
super(InvalidBaseError, self).__str__(),
"ID: " + unicode(self.id)
])
:type message: str
:param message: A message to display when raising the exception.

def loadable(func_name):
"""Decorator for getters that require a load() upon first access.
"""
super(Error, self).__init__()
self.message = message

:type func_name: function
:param func_name: class method that requires that load() be called if the class's _attribute value is None
def __str__(self):
return unicode(self.message) if self.message is not None else u""

:rtype: function
:return: the decorated class method.

"""
def inner(func):
cached_name = '_' + func.__name__
@functools.wraps(func)
def _decorator(self, *args, **kwargs):
if getattr(self, cached_name) is None:
getattr(self, func_name)()
return func(self, *args, **kwargs)
return _decorator
return inner
class MalformedPageError(Error):
"""Indicates that a page on MAL has broken markup in some way.
"""

class Base(object):
"""Abstract base class for MAL resources. Provides autoloading, auto-setting functionality for other MAL objects.
"""
__metaclass__ = abc.ABCMeta
def __init__(self, id, html, message=None):
super(MalformedPageError, self).__init__(message=message)
if isinstance(id, unicode):
self.id = id
else:
self.id = str(id).decode(u'utf-8')
if isinstance(html, unicode):
self.html = html
else:
self.html = str(html).decode(u'utf-8')

def __str__(self):
return "\n".join([
super(MalformedPageError, self).__str__(),
"ID: " + self.id,
"HTML: " + self.html
]).encode(u'utf-8')

"""Attribute name for primary reference key to this object.
When an attribute by the name given by _id_attribute is passed into set(), set() doesn't prepend an underscore for load()ing.
"""
_id_attribute = "id"

def __repr__(self):
return u"".join([
"<",
self.__class__.__name__,
" ",
self._id_attribute,
": ",
unicode(getattr(self, self._id_attribute)),
">"
])
class InvalidBaseError(Error):
"""Indicates that the particular resource instance requested does not exist on MAL.
"""

def __hash__(self):
return hash('-'.join([self.__class__.__name__, unicode(getattr(self, self._id_attribute))]))
def __init__(self, id, message=None):
super(InvalidBaseError, self).__init__(message=message)
self.id = id

def __eq__(self, other):
return isinstance(other, self.__class__) and getattr(self, self._id_attribute) == getattr(other, other._id_attribute)
def __str__(self):
return "\n".join([
super(InvalidBaseError, self).__str__(),
"ID: " + unicode(self.id)
])

def __ne__(self, other):
return not self.__eq__(other)

def __init__(self, session):
"""Create an instance of Base.
def loadable(func_name):
"""Decorator for getters that require a load() upon first access.

:type session: :class:`myanimelist.session.Session`
:param session: A valid MAL session.
:type func_name: function
:param func_name: class method that requires that load() be called if the class's _attribute value is None

"""
self.session = session
:rtype: function
:return: the decorated class method.

@abc.abstractmethod
def load(self):
"""A callback to run before any @loadable attributes are returned.
"""
pass

def set(self, attr_dict):
"""Sets attributes of this user object.
def inner(func):
cached_name = '_' + func.__name__

@functools.wraps(func)
def _decorator(self, *args, **kwargs):
if getattr(self, cached_name) is None:
getattr(self, func_name)()
return func(self, *args, **kwargs)

:type attr_dict: dict
:param attr_dict: Parameters to set, with attribute keys.
return _decorator

:rtype: :class:`.Base`
:return: The current object.
return inner


class Base(object):
"""Abstract base class for MAL resources. Provides autoloading, auto-setting functionality for other MAL objects.
"""
__metaclass__ = abc.ABCMeta

"""Attribute name for primary reference key to this object.
When an attribute by the name given by _id_attribute is passed into set(), set() doesn't prepend an underscore for load()ing.
"""
for key in attr_dict:
if key == self._id_attribute:
setattr(self, self._id_attribute, attr_dict[key])
else:
setattr(self, u"_" + key, attr_dict[key])
return self
_id_attribute = "id"

def __repr__(self):
return u"".join([
"<",
self.__class__.__name__,
" ",
self._id_attribute,
": ",
unicode(getattr(self, self._id_attribute)),
">"
])

def __hash__(self):
return hash('-'.join([self.__class__.__name__, unicode(getattr(self, self._id_attribute))]))

def __eq__(self, other):
return isinstance(other, self.__class__) and getattr(self, self._id_attribute) == getattr(other,
other._id_attribute)

def __ne__(self, other):
return not self.__eq__(other)

def __init__(self, session):
"""Create an instance of Base.

:type session: :class:`myanimelist.session.Session`
:param session: A valid MAL session.

"""
self.session = session

@abc.abstractmethod
def load(self):
"""A callback to run before any @loadable attributes are returned.
"""
pass

def set(self, attr_dict):
"""Sets attributes of this user object.

:type attr_dict: dict
:param attr_dict: Parameters to set, with attribute keys.

:rtype: :class:`.Base`
:return: The current object.

"""
for key in attr_dict:
if key == self._id_attribute:
setattr(self, self._id_attribute, attr_dict[key])
else:
setattr(self, u"_" + key, attr_dict[key])
return self
Loading