All pastes #792661 Raw Edit

UserCrate

public python v1 · immutable
·published 2007-11-21 16:38 UTC
rendered paste body
from silan.entities import userfrom silan.lines import description, kick, message, mode, nickchange, topic, userjoin, userpart, userquitclass UserCrate(object):	"""	UserCrate is a class to perform elegant tracking of IRC users and the	nicknames they are known as.		In previous iterations of silan, the maintenance of users was handled	by the generation of a massive list. All operations down to locating a	single user was done very neatly and cleanly through modifications to	this list. While this was very storage-light, it turned out to require	a massive amount of computational power to use.		While UserCrate also maintains a list of all users, it also has two	dictionaries. One contains nicks:users, and the other contains	addresses:users. Instead of iterating through the user list	(called "pool"), a user is found by looking up whatever information is	handy through the two dictionaries. If the information can't be found,	new dictionary entries are made to point at a new user. If new data is	made available about an existing user, the UserCrate intelligently	fills in the gaps with new dictionary entries pointing to a single	user instance.		The list of users is only referred to if necessary to obtain all users	at once, or to do advanced searching of user information.		In practice, this is a far more efficient method of handling users. The	trade-off is increased memory usage.	"""		def __init__(self):		self.pool = []				self.lu_nick = {}		self.lu_address = {}				self.ownuser = None			def _get_user(self, nick, address=None):		"""		nick: nickname of user		address: (optional) user@host of user				Same as find_user (even calls it for the same functionality),		but will add a user if they don't already exist. This is a 		"private" method in order to avoid it being used outside of		contexts of initialization. find_user should be used for		everything else. (It is not really "private" as it is		used by one or more external functions called during log		initialization.)		"""		inpool = self.find_user(nick, address)				if not inpool:			return self.add_user(nick, address)		else:			inpool.tick_nick(nick)			return inpool		def find_user(self, nick, address=None):		"""		nick: nickname of user		address: (optional) user@host of user				Find a user by nick and/or address. If a nick xor address does		not exist in the dictionary, but the address xor nick		(respectively) does, this will also call add_alternate to fill		in the gap in the appropriate dictionary. This is a		general-purpose method and should be used by any output		function or plugin to get a user from whatever information is		available.		"""		if address:			fromnick = self.lu_nick.get(nick, None)			fromaddress = self.lu_address.get(address, None)						if fromnick and not fromaddress:				return self.add_alternate(fromnick, nick, address)			elif fromaddress and not fromnick:				return self.add_alternate(fromaddress, nick, address)			else:				return fromnick					else:			return self.lu_nick.get(nick, None)				def add_user(self, nick, address=None):		newuser = user.User(nick, address)				if address:			self.lu_address[address] = newuser				self.lu_nick[nick] = newuser				self.pool.append(newuser)				return newuser			def merge_into(self, inuser, outuser):		"""		inuser: User that will be consumed and removed		outuser: User that will be kept, merged with inuser				Effectively turns two user instances into one. Will also		redefine appropriate dictionary entries in the lookup dicts to		all point to the one merged user.		"""		outuser.merge(inuser)				for addr in (addr for addr in self.lu_address if self.lu_address[addr] == inuser):			self.lu_address[addr] = outuser				for nick in (nick for nick in self.lu_nick if self.lu_nick[nick] == inuser):			self.lu_nick[nick] = outuser					self.pool.remove(inuser)				return outuser			def add_alternate(self, user, altnick, altaddress=None):		"""		user: Existing user instance		altnick: Additional nickname that will belong to the user		altaddress: (optional) Additional user@host for the user				Registers a new nickname or address with an existing user		instance. This can be called regardless of whether or not the		nick and/or host has been previously registered. Effectively,		this will 'tick' the nick as having been used.		"""				user.add_nick(altnick)				if altaddress:			user.add_address(altaddress)				self.lu_nick[altnick] = user				if altaddress:			self.lu_address[altaddress] = user					return user			def juggle_alternates(self, newnick, oldnick=None, address=None):		"""		newnick: New nickname user has changed to		oldnick: (optional) old nickname. None if local user.		address: (optional) address associated with old nick. 				Performs nick change handling. Creates new user if neither the		old or new nicks are recognized. Merges two users together if		a person changes their nick to one that originally belonged to		a separate user instance. Registers alternate nicks with		existing users.		"""				# If oldnick is None, skip the latter half of this method and		# recognize this as the user who the log belongs to.		if oldnick:			olduser = self.find_user(oldnick, address)		elif self.ownuser:			return self.add_alternate(self.ownuser, newnick)		else:			self.ownuser = self._get_user(newnick)			return self.ownuser				newuser = self.find_user(newnick)				if olduser and not newuser:			return self.add_alternate(olduser, newnick)		elif newuser and not olduser:			return self.add_alternate(newuser, oldnick, address)		elif olduser and newuser and olduser != newuser:			# Merges two separate user instances together			merged = self.merge_into(newuser, olduser)			merged.tick_nick(newnick)			return merged		elif olduser and newuser and olduser == newuser:			# If both nicks are already recognized and belong to			# the same user instance, 'tick' the new nick.			newuser.tick_nick(newnick)			return newuser		elif not olduser and not newuser:			# If neither nick has been seen before, make a new			# user instance.			rolluser = self._get_user(oldnick, address)			return self.add_alternate(rolluser, newnick)			def extract_users_into(line, crate):	"""	line: An instance of a silan.lines package class as contained in a Log	crate: The UserCrate nick tracking should occur in		This is the core of nick tracking operations for UserCrate. All Log	lines will ideally be passed through this function to create new users	and to update assignments of nicknames and addresses to user instances.		Also, every type of line will ultimately trigger a nickname 'tick'.	This is defined as incrementing the usage count of a particular nick by	1. The purpose of this is to determine the primary nick of a user based	on how frequently lines are logged with a specific nickname. The most	frequently used nick for a user will be its 'primary'. All other nicks	will be its 'alternates'.	"""		description, kick, message, mode, nickchange, topic, userjoin, userpart, userquit		if isinstance(line, description.Description):		crate._get_user(line.nick)	elif isinstance(line, kick.Kick):		crate._get_user(line.nick, line.address)		crate._get_user(line.target)	elif isinstance(line, message.Message):		crate._get_user(line.nick, line.address)	elif isinstance(line, mode.Mode):		crate._get_user(line.nick, line.address)				if isinstance(line, mode.ChannelSetAdmin) or isinstance(line, mode.ChannelSetHalfOp) or isinstance(line, mode.ChannelSetOp) or isinstance(line, mode.ChannelSetOwner) or isinstance(line, mode.ChannelUnsetAdmin) or isinstance(line, mode.ChannelUnsetHalfOp) or isinstance(line, mode.ChannelSetOp) or isinstance(line, mode.ChannelSetOwner):			crate._get_user(line.target)	elif isinstance(line, nickchange.NickChange):		crate.juggle_alternates(line.newnick, line.nick, line.address)	elif isinstance(line, topic.Topic):		crate._get_user(line.nick, line.address)	elif isinstance(line, userjoin.UserJoin):		crate._get_user(line.nick, line.address)	elif isinstance(line, userpart.UserPart):		crate._get_user(line.nick, line.address)	elif isinstance(line, userquit.UserQuit):		crate._get_user(line.nick, line.address)