<commit_before>from twisted.words.protocols import irc
from txircd.modbase import Command
from txircd.utils import irc_lower, epoch

class ModeCommand(Command):
	def onUse(self, user, data):
		if "targetchan" in data:
			self.chanUse(user, data["targetchan"], data["modes"])
		elif "targetuser" in data:
			if user == data["targetuser"]:
				self.userUse(user, data["modes"])
			else:
				user.sendMessage(irc.ERR_USERSDONTMATCH, ":Can't operate on modes for other users")

	def chanUse(self, user, channel, modes):
		if not modes:
			user.sendMessage(irc.RPL_CHANNELMODEIS, channel.name, channel.modeString(user))
			user.sendMessage(irc.RPL_CREATIONTIME, channel.name, str(epoch(channel.created)))
			return
		modeDisplay = []
		for modedata in modes:
			modetype, adding, mode, param = modedata
			if not (modetype == 0 and param is None): # ignore these checks for listing list modes
				if not adding and modetype >= 0 and mode not in channel.mode:
					continue # channel does not have mode set; cannot remove
				if adding:
					allowed, param = self.ircd.channel_modes[modetype][mode].checkSet(user, channel, param) if modetype >= 0 else self.ircd.prefixes[mode][2].checkSet(user, channel, param)
					if not allowed:
						continue
				else:
					allowed, param = self.ircd.channel_modes[modetype][mode].checkUnset(user, channel, param) if modetype >= 0 else self.ircd.prefixes[mode][2].checkUnset(user, channel, param)
					if not allowed:
						continue
			modedata[3] = param # update the param in modedata so that the displayed mode change is shown correctly
			if modetype == -1:
				if param not in self.ircd.users:
					continue
				udata = self.ircd.users[param]
				if channel.name not in udata.channels:
					continue
				if adding:
					status = udata.channels[channel.name]["status"]
					statusList = list(status)
					for index, statusLevel in enumerate(status):
						if self.ircd.prefixes[statusLevel][1] < self.ircd.prefixes[mode][1]:
							statusList.insert(index, mode)
							break
					if mode not in statusList: # no status to put this one before was found, so this goes at the end
						statusList.append(mode)
					udata.channels[channel.name]["status"] = "".join(statusList)
					modeDisplay.append(modedata)
				else:
					if mode in udata.channels[channel.name]["status"]:
						udata.channels[channel.name]["status"].remove(mode)
						modeDisplay.append(modedata)
			elif modetype == 0:
				if not param:
					self.ircd.channel_modes[modetype][mode].showParam(user, channel)
				elif adding:
					if mode not in channel.mode:
						channel.mode[mode] = []
					if param not in channel.mode[mode]:
						channel.mode[mode].append(param)
						modeDisplay.append(modedata)
				else:
					if mode not in channel.mode:
						continue
					if param in channel.mode[mode]:
						channel.mode[mode].remove(param)
						modeDisplay.append(modedata)
						if not channel.mode[mode]:
							del channel.mode[mode]
			else:
				if adding:
					if mode in channel.mode and param == channel.mode[mode]:
						continue
					channel.mode[mode] = param
					modeDisplay.append(modedata)
				else:
					if mode not in channel.mode:
						continue
					if modetype == 1 and param != channel.mode[mode]:
						continue
					del channel.mode[mode]
					modeDisplay.append(modedata)
		if modeDisplay:
			adding = None
			modestring = [] # use an array of characters since we're repeatedly adding to this; join at the end
			params = []
			for mode in modeDisplay:
				if mode[1] and adding != "+":
					adding = "+"
					modestring.append("+")
				elif not mode[1] and adding != "-":
					adding = "-"
					modestring.append("-")
				modestring.append(mode[2])
				if mode[3]:
					params.append(mode[3])
			modeLine = "{} {}".format("".join(modestring), " ".join(params)) if params else "".join(modestring)
			for udata in channel.users:
				udata.sendMessage("MODE", modeLine, to=channel.name, prefix=user.prefix())

	def userUse(self, user, modes):
		if not modes:
			user.sendMessage(irc.RPL_UMODEIS, user.modeString(user))
			return
		modeDisplay = []
		for modedata in modes:
			modetype, adding, mode, param = modedata
			if not (modetype == 0 and param is None): # ignore these checks for listing list modes
				if not adding and mode not in user.mode:
					continue # Cannot unset mode that's not set
				if adding:
					allowed, param = self.ircd.user_modes[modetype][mode].checkSet(user, user, param)
					if not allowed:
						continue
				else:
					allowed, param = self.ircd.user_modes[modetype][mode].checkUnset(user, user, param)
					if not allowed:
						continue
			modedata[3] = param # update the param in modedata so that the displayed mode change is shown correctly
			if modetype == 0:
				if not param:
					self.ircd.user_modes[modetype][mode].showParam(user, user)
				elif adding:
					if mode not in user.mode:
						user.mode[mode] = []
					if param not in user.mode[mode]:
						user.mode[mode].append(param)
						modeDisplay.append(modedata)
				else:
					if mode not in user.mode:
						continue
					if param in user.mode[mode]:
						user.mode[mode].remove(param)
						modeDisplay.append(modedata)
						if not user.mode[mode]:
							del user.mode[mode]
			else:
				if adding:
					if mode in user.mode and param == user.mode[mode]:
						continue
					user.mode[mode] = param
					modeDisplay.append(modedata)
				else:
					if mode not in user.mode:
						continue
					if modetype == 1 and param != user.mode[mode]:
						continue
					del user.mode[mode]
					modeDisplay.append(modedata)
		if modeDisplay:
			adding = None
			modestring = [] # use an array of characters since we're repeatedly adding to this; join at the end
			params = []
			for mode in modeDisplay:
				if mode[1] and adding != "+":
					adding = "+"
					modestring.append("+")
				elif not mode[1] and adding != "-":
					adding = "-"
					modestring.append("-")
				modestring.append(mode[2])
				if mode[3]:
					params.append(mode[3])
			modeLine = "{} {}".format("".join(modestring), " ".join(params)) if params else "".join(modestring)
			user.sendMessage("MODE", modeLine, prefix=user.prefix())

	def processParams(self, user, params):
		if user.registered > 0:
			user.sendMessage(irc.ERR_NOTREGISTERED, "MODE", ":You have not registered")
			return {}
		if not params:
			user.sendMessage(irc.ERR_NEEDMOREPARAMS, "MODE", ":Not enough parameters")
			return {}
		if params[0] in self.ircd.users:
			modeChanges = []
			if len(params) > 1 and params[1]:
				adding = True
				current_param = 2
				for mode in params[1]:
					if mode == "+":
						adding = True
					elif mode == "-":
						adding = False
					else:
						if mode not in self.ircd.user_mode_type:
							user.sendMessage(irc.ERR_UMODEUNKNOWNFLAG, mode, ":is unknown mode char to me")
							continue
						mode_type = self.ircd.user_mode_type[mode]
						if mode_type == 1 or (adding and mode_type == 2) or (mode_type == 0 and len(params) > current_param):
							if len(params) <= current_param:
								continue # Mode must have param that wasn't provided
							modeChanges.append([mode_type, adding, mode, params[current_param]])
							current_param += 1
						else:
							modeChanges.append([mode_type, adding, mode, None])
			return {
				"user": user,
				"targetuser": self.ircd.users[params[0]],
				"modes": modeChanges
			}
		if params[0] in self.ircd.channels:
			cdata = self.ircd.channels[params[0]]
			modeChanges = []
			if len(params) > 1 and params[1]:
				if params[0] not in user.channels or not user.hasAccess(cdata.name, self.ircd.servconfig["channel_minimum_level"]["MODE"]):
					if len(params) > 2:
						user.sendMessage(irc.ERR_CHANOPRIVSNEEDED, params[0], ":You must have channel operator access to set channel modes")
						return {}
					for mode in params[1]:
						if mode == "+" or mode == "-":
							continue
						if self.ircd.channel_mode_type[mode] != 0:
							user.sendMessage(irc.ERR_CHANOPRIVSNEEDED, params[0], ":You must have channel operator access to set channel modes")
							return {} # user is trying to change modes; abort
					# User isn't trying to change modes; go ahead and continue
				adding = True
				current_param = 2
				for mode in params[1]:
					if mode == "+":
						adding = True
					elif mode == "-":
						adding = False
					else:
						if mode not in self.ircd.channel_mode_type:
							user.sendMessage(irc.ERR_UNKNOWNMODE, mode, ":is unknown mode char to me")
							continue
						mode_type = self.ircd.channel_mode_type[mode]
						if mode_type == -1 or (mode_type == 0 and len(params) > current_param) or mode_type == 1 or (adding and mode_type == 2):
							if len(params) <= current_param:
								continue # Mode must have param that wasn't provided
							modeChanges.append([mode_type, adding, mode, params[current_param]])
							current_param += 1
						else:
							modeChanges.append([mode_type, adding, mode, None])
			return {
				"user": user,
				"targetchan": cdata,
				"modes": modeChanges
			}
		user.sendMessage(irc.ERR_NOSUCHNICK, params[0], ":No such nick/channel")
		return {}

class Spawner(object):
	def __init__(self, ircd):
		self.ircd = ircd

	def spawn(self):
		if "channel_minimum_level" not in self.ircd.servconfig:
			self.ircd.servconfig["channel_minimum_level"] = {}
		if "MODE" not in self.ircd.servconfig["channel_minimum_level"]:
			self.ircd.servconfig["channel_minimum_level"]["MODE"] = "o"
		return {
			"commands": {
				"MODE": ModeCommand()
			}
		}

	def cleanup(self):
		del self.ircd.commands["MODE"]<commit_msg>Fix removing statuses<commit_after>from twisted.words.protocols import irc
from txircd.modbase import Command
from txircd.utils import irc_lower, epoch

class ModeCommand(Command):
	def onUse(self, user, data):
		if "targetchan" in data:
			self.chanUse(user, data["targetchan"], data["modes"])
		elif "targetuser" in data:
			if user == data["targetuser"]:
				self.userUse(user, data["modes"])
			else:
				user.sendMessage(irc.ERR_USERSDONTMATCH, ":Can't operate on modes for other users")

	def chanUse(self, user, channel, modes):
		if not modes:
			user.sendMessage(irc.RPL_CHANNELMODEIS, channel.name, channel.modeString(user))
			user.sendMessage(irc.RPL_CREATIONTIME, channel.name, str(epoch(channel.created)))
			return
		modeDisplay = []
		for modedata in modes:
			modetype, adding, mode, param = modedata
			if not (modetype == 0 and param is None): # ignore these checks for listing list modes
				if not adding and modetype >= 0 and mode not in channel.mode:
					continue # channel does not have mode set; cannot remove
				if adding:
					allowed, param = self.ircd.channel_modes[modetype][mode].checkSet(user, channel, param) if modetype >= 0 else self.ircd.prefixes[mode][2].checkSet(user, channel, param)
					if not allowed:
						continue
				else:
					allowed, param = self.ircd.channel_modes[modetype][mode].checkUnset(user, channel, param) if modetype >= 0 else self.ircd.prefixes[mode][2].checkUnset(user, channel, param)
					if not allowed:
						continue
			modedata[3] = param # update the param in modedata so that the displayed mode change is shown correctly
			if modetype == -1:
				if param not in self.ircd.users:
					continue
				udata = self.ircd.users[param]
				if channel.name not in udata.channels:
					continue
				if adding:
					status = udata.channels[channel.name]["status"]
					statusList = list(status)
					for index, statusLevel in enumerate(status):
						if self.ircd.prefixes[statusLevel][1] < self.ircd.prefixes[mode][1]:
							statusList.insert(index, mode)
							break
					if mode not in statusList: # no status to put this one before was found, so this goes at the end
						statusList.append(mode)
					udata.channels[channel.name]["status"] = "".join(statusList)
					modeDisplay.append(modedata)
				else:
					if mode in udata.channels[channel.name]["status"]:
						udata.channels[channel.name]["status"] = udata.channels[channel.name]["status"].replace(mode, "")
						modeDisplay.append(modedata)
			elif modetype == 0:
				if not param:
					self.ircd.channel_modes[modetype][mode].showParam(user, channel)
				elif adding:
					if mode not in channel.mode:
						channel.mode[mode] = []
					if param not in channel.mode[mode]:
						channel.mode[mode].append(param)
						modeDisplay.append(modedata)
				else:
					if mode not in channel.mode:
						continue
					if param in channel.mode[mode]:
						channel.mode[mode].remove(param)
						modeDisplay.append(modedata)
						if not channel.mode[mode]:
							del channel.mode[mode]
			else:
				if adding:
					if mode in channel.mode and param == channel.mode[mode]:
						continue
					channel.mode[mode] = param
					modeDisplay.append(modedata)
				else:
					if mode not in channel.mode:
						continue
					if modetype == 1 and param != channel.mode[mode]:
						continue
					del channel.mode[mode]
					modeDisplay.append(modedata)
		if modeDisplay:
			adding = None
			modestring = [] # use an array of characters since we're repeatedly adding to this; join at the end
			params = []
			for mode in modeDisplay:
				if mode[1] and adding != "+":
					adding = "+"
					modestring.append("+")
				elif not mode[1] and adding != "-":
					adding = "-"
					modestring.append("-")
				modestring.append(mode[2])
				if mode[3]:
					params.append(mode[3])
			modeLine = "{} {}".format("".join(modestring), " ".join(params)) if params else "".join(modestring)
			for udata in channel.users:
				udata.sendMessage("MODE", modeLine, to=channel.name, prefix=user.prefix())

	def userUse(self, user, modes):
		if not modes:
			user.sendMessage(irc.RPL_UMODEIS, user.modeString(user))
			return
		modeDisplay = []
		for modedata in modes:
			modetype, adding, mode, param = modedata
			if not (modetype == 0 and param is None): # ignore these checks for listing list modes
				if not adding and mode not in user.mode:
					continue # Cannot unset mode that's not set
				if adding:
					allowed, param = self.ircd.user_modes[modetype][mode].checkSet(user, user, param)
					if not allowed:
						continue
				else:
					allowed, param = self.ircd.user_modes[modetype][mode].checkUnset(user, user, param)
					if not allowed:
						continue
			modedata[3] = param # update the param in modedata so that the displayed mode change is shown correctly
			if modetype == 0:
				if not param:
					self.ircd.user_modes[modetype][mode].showParam(user, user)
				elif adding:
					if mode not in user.mode:
						user.mode[mode] = []
					if param not in user.mode[mode]:
						user.mode[mode].append(param)
						modeDisplay.append(modedata)
				else:
					if mode not in user.mode:
						continue
					if param in user.mode[mode]:
						user.mode[mode].remove(param)
						modeDisplay.append(modedata)
						if not user.mode[mode]:
							del user.mode[mode]
			else:
				if adding:
					if mode in user.mode and param == user.mode[mode]:
						continue
					user.mode[mode] = param
					modeDisplay.append(modedata)
				else:
					if mode not in user.mode:
						continue
					if modetype == 1 and param != user.mode[mode]:
						continue
					del user.mode[mode]
					modeDisplay.append(modedata)
		if modeDisplay:
			adding = None
			modestring = [] # use an array of characters since we're repeatedly adding to this; join at the end
			params = []
			for mode in modeDisplay:
				if mode[1] and adding != "+":
					adding = "+"
					modestring.append("+")
				elif not mode[1] and adding != "-":
					adding = "-"
					modestring.append("-")
				modestring.append(mode[2])
				if mode[3]:
					params.append(mode[3])
			modeLine = "{} {}".format("".join(modestring), " ".join(params)) if params else "".join(modestring)
			user.sendMessage("MODE", modeLine, prefix=user.prefix())

	def processParams(self, user, params):
		if user.registered > 0:
			user.sendMessage(irc.ERR_NOTREGISTERED, "MODE", ":You have not registered")
			return {}
		if not params:
			user.sendMessage(irc.ERR_NEEDMOREPARAMS, "MODE", ":Not enough parameters")
			return {}
		if params[0] in self.ircd.users:
			modeChanges = []
			if len(params) > 1 and params[1]:
				adding = True
				current_param = 2
				for mode in params[1]:
					if mode == "+":
						adding = True
					elif mode == "-":
						adding = False
					else:
						if mode not in self.ircd.user_mode_type:
							user.sendMessage(irc.ERR_UMODEUNKNOWNFLAG, mode, ":is unknown mode char to me")
							continue
						mode_type = self.ircd.user_mode_type[mode]
						if mode_type == 1 or (adding and mode_type == 2) or (mode_type == 0 and len(params) > current_param):
							if len(params) <= current_param:
								continue # Mode must have param that wasn't provided
							modeChanges.append([mode_type, adding, mode, params[current_param]])
							current_param += 1
						else:
							modeChanges.append([mode_type, adding, mode, None])
			return {
				"user": user,
				"targetuser": self.ircd.users[params[0]],
				"modes": modeChanges
			}
		if params[0] in self.ircd.channels:
			cdata = self.ircd.channels[params[0]]
			modeChanges = []
			if len(params) > 1 and params[1]:
				if params[0] not in user.channels or not user.hasAccess(cdata.name, self.ircd.servconfig["channel_minimum_level"]["MODE"]):
					if len(params) > 2:
						user.sendMessage(irc.ERR_CHANOPRIVSNEEDED, params[0], ":You must have channel operator access to set channel modes")
						return {}
					for mode in params[1]:
						if mode == "+" or mode == "-":
							continue
						if self.ircd.channel_mode_type[mode] != 0:
							user.sendMessage(irc.ERR_CHANOPRIVSNEEDED, params[0], ":You must have channel operator access to set channel modes")
							return {} # user is trying to change modes; abort
					# User isn't trying to change modes; go ahead and continue
				adding = True
				current_param = 2
				for mode in params[1]:
					if mode == "+":
						adding = True
					elif mode == "-":
						adding = False
					else:
						if mode not in self.ircd.channel_mode_type:
							user.sendMessage(irc.ERR_UNKNOWNMODE, mode, ":is unknown mode char to me")
							continue
						mode_type = self.ircd.channel_mode_type[mode]
						if mode_type == -1 or (mode_type == 0 and len(params) > current_param) or mode_type == 1 or (adding and mode_type == 2):
							if len(params) <= current_param:
								continue # Mode must have param that wasn't provided
							modeChanges.append([mode_type, adding, mode, params[current_param]])
							current_param += 1
						else:
							modeChanges.append([mode_type, adding, mode, None])
			return {
				"user": user,
				"targetchan": cdata,
				"modes": modeChanges
			}
		user.sendMessage(irc.ERR_NOSUCHNICK, params[0], ":No such nick/channel")
		return {}

class Spawner(object):
	def __init__(self, ircd):
		self.ircd = ircd

	def spawn(self):
		if "channel_minimum_level" not in self.ircd.servconfig:
			self.ircd.servconfig["channel_minimum_level"] = {}
		if "MODE" not in self.ircd.servconfig["channel_minimum_level"]:
			self.ircd.servconfig["channel_minimum_level"]["MODE"] = "o"
		return {
			"commands": {
				"MODE": ModeCommand()
			}
		}

	def cleanup(self):
		del self.ircd.commands["MODE"]<|endoftext|>


<commit_before>from django.http import HttpResponse
from django.shortcuts import render_to_response

def home(request):
	return render_to_response('index.html', {})
<commit_msg>added profile view<commit_after>from django.http import HttpResponse
from django.shortcuts import render_to_response
import json

from accounts import manager
from utils import platform_config
from django.contrib.auth.decorators import login_required

def home(request):
	return render_to_response('index.html', {})

@login_required
def profile(request):
	values = {}
	values['user'] = manager.getUser(request.user)
	values['connector_link'] = {}
	values['connector_link']['cas'] = platform_config.IDENTITY_PROVIDERS['CAS']['link']
	return render_to_response('profile.html', values)
<|endoftext|>


<commit_before>from _locale import (setlocale, LC_ALL, LC_CTYPE, LC_NUMERIC, localeconv, Error)
try:
    from _locale import (RADIXCHAR, THOUSEP, nl_langinfo)
except ImportError:
    nl_langinfo = None

import codecs
import locale
import sys
import unittest
from platform import uname
from test.support import run_unittest

if uname()[0] == "Darwin":
    maj, min, mic = [int(part) for part in uname()[2].split(".")]
    if (maj, min, mic) < (8, 0, 0):
        raise unittest.SkipTest("locale support broken for OS X < 10.4")

candidate_locales = ['es_UY', 'fr_FR', 'fi_FI', 'es_CO', 'pt_PT', 'it_IT',
    'et_EE', 'es_PY', 'no_NO', 'nl_NL', 'lv_LV', 'el_GR', 'be_BY', 'fr_BE',
    'ro_RO', 'ru_UA', 'ru_RU', 'es_VE', 'ca_ES', 'se_NO', 'es_EC', 'id_ID',
    'ka_GE', 'es_CL', 'wa_BE', 'hu_HU', 'lt_LT', 'sl_SI', 'hr_HR', 'es_AR',
    'es_ES', 'oc_FR', 'gl_ES', 'bg_BG', 'is_IS', 'mk_MK', 'de_AT', 'pt_BR',
    'da_DK', 'nn_NO', 'cs_CZ', 'de_LU', 'es_BO', 'sq_AL', 'sk_SK', 'fr_CH',
    'de_DE', 'sr_YU', 'br_FR', 'nl_BE', 'sv_FI', 'pl_PL', 'fr_CA', 'fo_FO',
    'bs_BA', 'fr_LU', 'kl_GL', 'fa_IR', 'de_BE', 'sv_SE', 'it_CH', 'uk_UA',
    'eu_ES', 'vi_VN', 'af_ZA', 'nb_NO', 'en_DK', 'tg_TJ', 'en_US',
    'es_ES.ISO8859-1', 'fr_FR.ISO8859-15', 'ru_RU.KOI8-R', 'ko_KR.eucKR']

# Issue #13441: Skip some locales (e.g. cs_CZ and hu_HU) on Solaris to
# workaround a mbstowcs() bug. For example, on Solaris, the hu_HU locale uses
# the locale encoding ISO-8859-2, the thousauds separator is b'\xA0' and it is
# decoded as U+30000020 (an invalid character) by mbstowcs().
if sys.platform == 'sunos5':
    old_locale = locale.setlocale(locale.LC_ALL)
    try:
        locales = []
        for loc in candidate_locales:
            try:
                locale.setlocale(locale.LC_ALL, loc)
            except Error:
                continue
            encoding = locale.getpreferredencoding(False)
            try:
                localeconv()
            except Exception as err:
                print("WARNING: Skip locale %s (encoding %s): [%s] %s"
                      % (loc, encoding, type(err), err))
            else:
                locales.append(loc)
        #candidate_locales = locales
    finally:
        locale.setlocale(locale.LC_ALL, old_locale)

# Workaround for MSVC6(debug) crash bug
if "MSC v.1200" in sys.version:
    def accept(loc):
        a = loc.split(".")
        return not(len(a) == 2 and len(a[-1]) >= 9)
    candidate_locales = [loc for loc in candidate_locales if accept(loc)]

# List known locale values to test against when available.
# Dict formatted as ``<locale> : (<decimal_point>, <thousands_sep>)``.  If a
# value is not known, use '' .
known_numerics = {'fr_FR' : (',', ''), 'en_US':('.', ',')}

class _LocaleTests(unittest.TestCase):

    def setUp(self):
        self.oldlocale = setlocale(LC_ALL)

    def tearDown(self):
        setlocale(LC_ALL, self.oldlocale)

    # Want to know what value was calculated, what it was compared against,
    # what function was used for the calculation, what type of data was used,
    # the locale that was supposedly set, and the actual locale that is set.
    lc_numeric_err_msg = "%s != %s (%s for %s; set to %s, using %s)"

    def numeric_tester(self, calc_type, calc_value, data_type, used_locale):
        """Compare calculation against known value, if available"""
        try:
            set_locale = setlocale(LC_NUMERIC)
        except Error:
            set_locale = "<not able to determine>"
        known_value = known_numerics.get(used_locale,
                                    ('', ''))[data_type == 'thousands_sep']
        if known_value and calc_value:
            self.assertEqual(calc_value, known_value,
                                self.lc_numeric_err_msg % (
                                    calc_value, known_value,
                                    calc_type, data_type, set_locale,
                                    used_locale))

    @unittest.skipUnless(nl_langinfo, "nl_langinfo is not available")
    def test_lc_numeric_nl_langinfo(self):
        # Test nl_langinfo against known values
        for loc in candidate_locales:
            try:
                setlocale(LC_NUMERIC, loc)
                setlocale(LC_CTYPE, loc)
            except Error:
                continue
            for li, lc in ((RADIXCHAR, "decimal_point"),
                            (THOUSEP, "thousands_sep")):
                self.numeric_tester('nl_langinfo', nl_langinfo(li), lc, loc)

    def test_lc_numeric_localeconv(self):
        # Test localeconv against known values
        for loc in candidate_locales:
            try:
                setlocale(LC_NUMERIC, loc)
                setlocale(LC_CTYPE, loc)
            except Error:
                continue
            formatting = localeconv()
            for lc in ("decimal_point",
                        "thousands_sep"):
                self.numeric_tester('localeconv', formatting[lc], lc, loc)

    @unittest.skipUnless(nl_langinfo, "nl_langinfo is not available")
    def test_lc_numeric_basic(self):
        # Test nl_langinfo against localeconv
        for loc in candidate_locales:
            try:
                setlocale(LC_NUMERIC, loc)
                setlocale(LC_CTYPE, loc)
            except Error:
                continue
            for li, lc in ((RADIXCHAR, "decimal_point"),
                            (THOUSEP, "thousands_sep")):
                nl_radixchar = nl_langinfo(li)
                li_radixchar = localeconv()[lc]
                try:
                    set_locale = setlocale(LC_NUMERIC)
                except Error:
                    set_locale = "<not able to determine>"
                self.assertEqual(nl_radixchar, li_radixchar,
                                "%s (nl_langinfo) != %s (localeconv) "
                                "(set to %s, using %s)" % (
                                                nl_radixchar, li_radixchar,
                                                loc, set_locale))

    def test_float_parsing(self):
        # Bug #1391872: Test whether float parsing is okay on European
        # locales.
        for loc in candidate_locales:
            try:
                setlocale(LC_NUMERIC, loc)
                setlocale(LC_CTYPE, loc)
            except Error:
                continue

            # Ignore buggy locale databases. (Mac OS 10.4 and some other BSDs)
            if loc == 'eu_ES' and localeconv()['decimal_point'] == "' ":
                continue

            self.assertEqual(int(eval('3.14') * 100), 314,
                                "using eval('3.14') failed for %s" % loc)
            self.assertEqual(int(float('3.14') * 100), 314,
                                "using float('3.14') failed for %s" % loc)
            if localeconv()['decimal_point'] != '.':
                self.assertRaises(ValueError, float,
                                  localeconv()['decimal_point'].join(['1', '23']))

def test_main():
    run_unittest(_LocaleTests)

if __name__ == '__main__':
    test_main()
<commit_msg>Issue #13441: Enable the workaround for Solaris locale bug<commit_after>from _locale import (setlocale, LC_ALL, LC_CTYPE, LC_NUMERIC, localeconv, Error)
try:
    from _locale import (RADIXCHAR, THOUSEP, nl_langinfo)
except ImportError:
    nl_langinfo = None

import codecs
import locale
import sys
import unittest
from platform import uname
from test.support import run_unittest

if uname()[0] == "Darwin":
    maj, min, mic = [int(part) for part in uname()[2].split(".")]
    if (maj, min, mic) < (8, 0, 0):
        raise unittest.SkipTest("locale support broken for OS X < 10.4")

candidate_locales = ['es_UY', 'fr_FR', 'fi_FI', 'es_CO', 'pt_PT', 'it_IT',
    'et_EE', 'es_PY', 'no_NO', 'nl_NL', 'lv_LV', 'el_GR', 'be_BY', 'fr_BE',
    'ro_RO', 'ru_UA', 'ru_RU', 'es_VE', 'ca_ES', 'se_NO', 'es_EC', 'id_ID',
    'ka_GE', 'es_CL', 'wa_BE', 'hu_HU', 'lt_LT', 'sl_SI', 'hr_HR', 'es_AR',
    'es_ES', 'oc_FR', 'gl_ES', 'bg_BG', 'is_IS', 'mk_MK', 'de_AT', 'pt_BR',
    'da_DK', 'nn_NO', 'cs_CZ', 'de_LU', 'es_BO', 'sq_AL', 'sk_SK', 'fr_CH',
    'de_DE', 'sr_YU', 'br_FR', 'nl_BE', 'sv_FI', 'pl_PL', 'fr_CA', 'fo_FO',
    'bs_BA', 'fr_LU', 'kl_GL', 'fa_IR', 'de_BE', 'sv_SE', 'it_CH', 'uk_UA',
    'eu_ES', 'vi_VN', 'af_ZA', 'nb_NO', 'en_DK', 'tg_TJ', 'en_US',
    'es_ES.ISO8859-1', 'fr_FR.ISO8859-15', 'ru_RU.KOI8-R', 'ko_KR.eucKR']

# Issue #13441: Skip some locales (e.g. cs_CZ and hu_HU) on Solaris to
# workaround a mbstowcs() bug. For example, on Solaris, the hu_HU locale uses
# the locale encoding ISO-8859-2, the thousauds separator is b'\xA0' and it is
# decoded as U+30000020 (an invalid character) by mbstowcs().
if sys.platform == 'sunos5':
    old_locale = locale.setlocale(locale.LC_ALL)
    try:
        locales = []
        for loc in candidate_locales:
            try:
                locale.setlocale(locale.LC_ALL, loc)
            except Error:
                continue
            encoding = locale.getpreferredencoding(False)
            try:
                localeconv()
            except Exception as err:
                print("WARNING: Skip locale %s (encoding %s): [%s] %s"
                      % (loc, encoding, type(err), err))
            else:
                locales.append(loc)
        candidate_locales = locales
    finally:
        locale.setlocale(locale.LC_ALL, old_locale)

# Workaround for MSVC6(debug) crash bug
if "MSC v.1200" in sys.version:
    def accept(loc):
        a = loc.split(".")
        return not(len(a) == 2 and len(a[-1]) >= 9)
    candidate_locales = [loc for loc in candidate_locales if accept(loc)]

# List known locale values to test against when available.
# Dict formatted as ``<locale> : (<decimal_point>, <thousands_sep>)``.  If a
# value is not known, use '' .
known_numerics = {'fr_FR' : (',', ''), 'en_US':('.', ',')}

class _LocaleTests(unittest.TestCase):

    def setUp(self):
        self.oldlocale = setlocale(LC_ALL)

    def tearDown(self):
        setlocale(LC_ALL, self.oldlocale)

    # Want to know what value was calculated, what it was compared against,
    # what function was used for the calculation, what type of data was used,
    # the locale that was supposedly set, and the actual locale that is set.
    lc_numeric_err_msg = "%s != %s (%s for %s; set to %s, using %s)"

    def numeric_tester(self, calc_type, calc_value, data_type, used_locale):
        """Compare calculation against known value, if available"""
        try:
            set_locale = setlocale(LC_NUMERIC)
        except Error:
            set_locale = "<not able to determine>"
        known_value = known_numerics.get(used_locale,
                                    ('', ''))[data_type == 'thousands_sep']
        if known_value and calc_value:
            self.assertEqual(calc_value, known_value,
                                self.lc_numeric_err_msg % (
                                    calc_value, known_value,
                                    calc_type, data_type, set_locale,
                                    used_locale))

    @unittest.skipUnless(nl_langinfo, "nl_langinfo is not available")
    def test_lc_numeric_nl_langinfo(self):
        # Test nl_langinfo against known values
        for loc in candidate_locales:
            try:
                setlocale(LC_NUMERIC, loc)
                setlocale(LC_CTYPE, loc)
            except Error:
                continue
            for li, lc in ((RADIXCHAR, "decimal_point"),
                            (THOUSEP, "thousands_sep")):
                self.numeric_tester('nl_langinfo', nl_langinfo(li), lc, loc)

    def test_lc_numeric_localeconv(self):
        # Test localeconv against known values
        for loc in candidate_locales:
            try:
                setlocale(LC_NUMERIC, loc)
                setlocale(LC_CTYPE, loc)
            except Error:
                continue
            formatting = localeconv()
            for lc in ("decimal_point",
                        "thousands_sep"):
                self.numeric_tester('localeconv', formatting[lc], lc, loc)

    @unittest.skipUnless(nl_langinfo, "nl_langinfo is not available")
    def test_lc_numeric_basic(self):
        # Test nl_langinfo against localeconv
        for loc in candidate_locales:
            try:
                setlocale(LC_NUMERIC, loc)
                setlocale(LC_CTYPE, loc)
            except Error:
                continue
            for li, lc in ((RADIXCHAR, "decimal_point"),
                            (THOUSEP, "thousands_sep")):
                nl_radixchar = nl_langinfo(li)
                li_radixchar = localeconv()[lc]
                try:
                    set_locale = setlocale(LC_NUMERIC)
                except Error:
                    set_locale = "<not able to determine>"
                self.assertEqual(nl_radixchar, li_radixchar,
                                "%s (nl_langinfo) != %s (localeconv) "
                                "(set to %s, using %s)" % (
                                                nl_radixchar, li_radixchar,
                                                loc, set_locale))

    def test_float_parsing(self):
        # Bug #1391872: Test whether float parsing is okay on European
        # locales.
        for loc in candidate_locales:
            try:
                setlocale(LC_NUMERIC, loc)
                setlocale(LC_CTYPE, loc)
            except Error:
                continue

            # Ignore buggy locale databases. (Mac OS 10.4 and some other BSDs)
            if loc == 'eu_ES' and localeconv()['decimal_point'] == "' ":
                continue

            self.assertEqual(int(eval('3.14') * 100), 314,
                                "using eval('3.14') failed for %s" % loc)
            self.assertEqual(int(float('3.14') * 100), 314,
                                "using float('3.14') failed for %s" % loc)
            if localeconv()['decimal_point'] != '.':
                self.assertRaises(ValueError, float,
                                  localeconv()['decimal_point'].join(['1', '23']))

def test_main():
    run_unittest(_LocaleTests)

if __name__ == '__main__':
    test_main()
<|endoftext|>


<commit_before>import time
import sys
from logging import info, debug, warn
from cStringIO import StringIO
import arch

from model import *
import basedir
from namespaces import *
import ConfigParser
import reader
import download
from iface_cache import iface_cache
from zeroinstall import NeedDownload

class Policy(object):
	__slots__ = ['root', 'implementation', 'watchers',
		     'help_with_testing', 'network_use',
		     'freshness', 'ready', 'handler', 'warned_offline']

	def __init__(self, root, handler = None):
		self.watchers = []
		self.help_with_testing = False
		self.network_use = network_full
		self.freshness = 60 * 60 * 24 * 7	# Seconds allowed since last update
		self.ready = False

		# If we need to download something but can't because we are offline,
		# warn the user. But only the first time.
		self.warned_offline = False

		# (allow self for backwards compat)
		self.handler = handler or self

		debug("Supported systems: '%s'", arch.os_ranks)
		debug("Supported processors: '%s'", arch.machine_ranks)

		path = basedir.load_first_config(config_site, config_prog, 'global')
		if path:
			try:
				config = ConfigParser.ConfigParser()
				config.read(path)
				self.help_with_testing = config.getboolean('global',
								'help_with_testing')
				self.network_use = config.get('global', 'network_use')
				self.freshness = int(config.get('global', 'freshness'))
				assert self.network_use in network_levels
			except Exception, ex:
				print >>sys.stderr, "Error loading config:", ex

		self.set_root(root)

		iface_cache.add_watcher(self)

	def set_root(self, root):
		assert isinstance(root, (str, unicode))
		self.root = root
		self.implementation = {}		# Interface -> [Implementation | None]

	def save_config(self):
		config = ConfigParser.ConfigParser()
		config.add_section('global')

		config.set('global', 'help_with_testing', self.help_with_testing)
		config.set('global', 'network_use', self.network_use)
		config.set('global', 'freshness', self.freshness)

		path = basedir.save_config_path(config_site, config_prog)
		path = os.path.join(path, 'global')
		config.write(file(path + '.new', 'w'))
		os.rename(path + '.new', path)

	def recalculate(self):
		self.implementation = {}
		self.ready = True
		debug("Recalculate! root = %s", self.root)
		def process(iface):
			if iface in self.implementation:
				debug("cycle; skipping second %s", iface)
				return
			self.implementation[iface] = None	# Avoid cycles

			impl = self._get_best_implementation(iface)
			if impl:
				debug("Will use implementation %s (version %s)", impl, impl.get_version())
				self.implementation[iface] = impl
				for d in impl.dependencies.values():
					debug("Considering dependency %s", d)
					process(self.get_interface(d.interface))
			else:
				debug("No implementation chould be chosen yet");
				self.ready = False
		process(self.get_interface(self.root))
		for w in self.watchers: w()

	# Only to be called from recalculate, as it is quite slow.
	# Use the results stored in self.implementation instead.
	def _get_best_implementation(self, iface):
		impls = iface.implementations.values()
		for f in self.usable_feeds(iface):
			debug("Processing feed %s", f)
			try:
				feed_iface = self.get_interface(f.uri)
				if feed_iface.name and iface.uri not in feed_iface.feed_for:
					warn("Missing <feed-for> for '%s' in '%s'",
						iface.uri, f.uri)
				if feed_iface.implementations:
					impls.extend(feed_iface.implementations.values())
			except NeedDownload, ex:
				raise ex
			except Exception, ex:
				warn("Failed to load feed %s for %s: %s",
					f, iface, str(ex))

		debug("get_best_implementation(%s), with feeds: %s", iface, iface.feeds)

		if not impls:
			info("Interface %s has no implementations!", iface)
			return None
		best = impls[0]
		for x in impls[1:]:
			if self.compare(iface, x, best) < 0:
				best = x
		if self.is_unusable(best):
			info("Best implementation of %s is %s, but unusable (%s)", iface, best,
							self.get_unusable_reason(best))
			return None
		return best

	def compare(self, interface, b, a):
		a_stab = a.get_stability()
		b_stab = b.get_stability()

		# Usable ones come first
		r = cmp(self.is_unusable(b), self.is_unusable(a))
		if r: return r

		# Preferred versions come first
		r = cmp(a_stab == preferred, b_stab == preferred)
		if r: return r

		if self.network_use != network_full:
			r = cmp(self.get_cached(a), self.get_cached(b))
			if r: return r

		# Stability
		stab_policy = interface.stability_policy
		if not stab_policy:
			if self.help_with_testing: stab_policy = testing
			else: stab_policy = stable

		if a_stab >= stab_policy: a_stab = preferred
		if b_stab >= stab_policy: b_stab = preferred

		r = cmp(a_stab, b_stab)
		if r: return r

		# Newer versions come before older ones
		r = cmp(a.version, b.version)
		if r: return r

		# Get best OS
		r = cmp(arch.os_ranks.get(a.os, None),
			arch.os_ranks.get(b.os, None))
		if r: return r

		# Get best machine
		r = cmp(arch.machine_ranks.get(a.machine, None),
			arch.machine_ranks.get(b.machine, None))
		if r: return r

		# Slightly prefer cached versions
		if self.network_use == network_full:
			r = cmp(self.get_cached(a), self.get_cached(b))
			if r: return r

		return cmp(a.id, b.id)

	def usable_feeds(self, iface):
		"""Generator for iface.feeds that are valid for our architecture."""
		for f in iface.feeds:
			if f.os in arch.os_ranks and f.machine in arch.machine_ranks:
				yield f
			else:
				debug("Skipping '%s'; unsupported architecture %s-%s",
					f, f.os, f.machine)

	def get_ranked_implementations(self, iface):
		impls = iface.implementations.values()
		for f in self.usable_feeds(iface):
			feed_iface = self.get_interface(f.uri)
			if feed_iface.implementations:
				impls.extend(feed_iface.implementations.values())
		impls.sort(lambda a, b: self.compare(iface, a, b))
		return impls

	def is_unusable(self, impl):
		return self.get_unusable_reason(impl) != None

	def get_unusable_reason(self, impl):
		"""Returns the reason why this impl is unusable, or None if it's OK"""
		stability = impl.get_stability()
		if stability <= buggy:
			return stability.name
		if self.network_use == network_offline and not self.get_cached(impl):
			return "Not cached and we are off-line"
		if impl.os not in arch.os_ranks:
			return "Unsupported OS"
		if impl.machine not in arch.machine_ranks:
			return "Unsupported machine type"
		return None

	def get_interface(self, uri):
		iface = iface_cache.get_interface(uri)

		if iface.last_modified is None:
			if self.network_use != network_offline:
				debug("Interface not cached and not off-line. Downloading...")
				self.begin_iface_download(iface)
			else:
				if self.warned_offline:
					debug("Nothing known about interface, but we are off-line.")
				else:
					warn("Nothing known about interface '%s', but we are in off-line mode "
						"(so not fetching)." % uri)
					self.warned_offline = True
		elif not uri.startswith('/'):
			staleness = time.time() - (iface.last_checked or 0)
			debug("Staleness for %s is %.2f hours", iface, staleness / 3600.0)

			if self.network_use != network_offline and self.freshness > 0 and staleness > self.freshness:
				debug("Updating %s", iface)
				self.begin_iface_download(iface, False)
		#else: debug("Local interface, so not checking staleness.")

		return iface

	def begin_iface_download(self, interface, force = False):
		debug("begin_iface_download %s (force = %d)", interface, force)
		if interface.uri.startswith('/'):
			return
		dl = download.begin_iface_download(interface, force)
		if not dl:
			assert not force
			debug("Already in progress")
			return

		debug("Need to download")
		# Calls update_interface_from_network eventually on success
		self.handler.monitor_download(dl)

	def get_implementation_path(self, impl):
		assert isinstance(impl, Implementation)
		if impl.id.startswith('/'):
			return impl.id
		return iface_cache.stores.lookup(impl.id)

	def get_implementation(self, interface):
		assert isinstance(interface, Interface)

		if not interface.name:
			raise SafeException("We don't have enough information to "
					    "run this program yet. "
					    "Need to download:\n%s" % interface.uri)
		try:
			return self.implementation[interface]
		except KeyError, ex:
			if interface.implementations:
				offline = ""
				if self.network_use == network_offline:
					offline = "\nThis may be because 'Network Use' is set to Off-line."
				raise SafeException("No usable implementation found for '%s'.%s" %
						(interface.name, offline))
			raise ex

	def walk_interfaces(self):
		# Deprecated
		return iter(self.implementation)

	def check_signed_data(self, download, signed_data):
		iface_cache.check_signed_data(download.interface, signed_data, self.handler)

	def get_cached(self, impl):
		if impl.id.startswith('/'):
			return os.path.exists(impl.id)
		else:
			try:
				path = self.get_implementation_path(impl)
				assert path
				return True
			except:
				pass # OK
		return False

	def add_to_cache(self, source, data):
		iface_cache.add_to_cache(source, data)

	def get_uncached_implementations(self):
		uncached = []
		for iface in self.implementation:
			impl = self.implementation[iface]
			assert impl
			if not self.get_cached(impl):
				uncached.append((iface, impl))
		return uncached

	def refresh_all(self, force = True):
		for x in self.walk_interfaces():
			self.begin_iface_download(x, force)
			for f in self.usable_feeds(x):
				feed_iface = self.get_interface(f.uri)
				self.begin_iface_download(feed_iface, force)

	def interface_changed(self, interface):
		debug("interface_changed(%s): recalculating", interface)
		self.recalculate()

	def get_feed_targets(self, feed_iface_uri):
		"""Return a list of Interfaces for which feed_iface can be a feed.
		This is used by --feed. If there are no interfaces, raises SafeException."""
		feed_iface = self.get_interface(feed_iface_uri)
		if not feed_iface.feed_for:
			if not feed_iface.name:
				raise SafeException("Can't get feed targets for '%s'; failed to load interface." %
						feed_iface_uri)
			raise SafeException("Missing <feed-for> element in '%s'; "
					"this interface can't be used as a feed." % feed_iface_uri)
		feed_targets = feed_iface.feed_for
		if not feed_iface.name:
			warn("Warning: unknown interface '%s'" % feed_iface_uri)
		return [self.get_interface(uri) for uri in feed_targets]
<commit_msg>Changed default freshness from one week to one month.<commit_after>import time
import sys
from logging import info, debug, warn
from cStringIO import StringIO
import arch

from model import *
import basedir
from namespaces import *
import ConfigParser
import reader
import download
from iface_cache import iface_cache
from zeroinstall import NeedDownload

class Policy(object):
	__slots__ = ['root', 'implementation', 'watchers',
		     'help_with_testing', 'network_use',
		     'freshness', 'ready', 'handler', 'warned_offline']

	def __init__(self, root, handler = None):
		self.watchers = []
		self.help_with_testing = False
		self.network_use = network_full
		self.freshness = 60 * 60 * 24 * 30	# Seconds allowed since last update (1 month)
		self.ready = False

		# If we need to download something but can't because we are offline,
		# warn the user. But only the first time.
		self.warned_offline = False

		# (allow self for backwards compat)
		self.handler = handler or self

		debug("Supported systems: '%s'", arch.os_ranks)
		debug("Supported processors: '%s'", arch.machine_ranks)

		path = basedir.load_first_config(config_site, config_prog, 'global')
		if path:
			try:
				config = ConfigParser.ConfigParser()
				config.read(path)
				self.help_with_testing = config.getboolean('global',
								'help_with_testing')
				self.network_use = config.get('global', 'network_use')
				self.freshness = int(config.get('global', 'freshness'))
				assert self.network_use in network_levels
			except Exception, ex:
				print >>sys.stderr, "Error loading config:", ex

		self.set_root(root)

		iface_cache.add_watcher(self)

	def set_root(self, root):
		assert isinstance(root, (str, unicode))
		self.root = root
		self.implementation = {}		# Interface -> [Implementation | None]

	def save_config(self):
		config = ConfigParser.ConfigParser()
		config.add_section('global')

		config.set('global', 'help_with_testing', self.help_with_testing)
		config.set('global', 'network_use', self.network_use)
		config.set('global', 'freshness', self.freshness)

		path = basedir.save_config_path(config_site, config_prog)
		path = os.path.join(path, 'global')
		config.write(file(path + '.new', 'w'))
		os.rename(path + '.new', path)

	def recalculate(self):
		self.implementation = {}
		self.ready = True
		debug("Recalculate! root = %s", self.root)
		def process(iface):
			if iface in self.implementation:
				debug("cycle; skipping second %s", iface)
				return
			self.implementation[iface] = None	# Avoid cycles

			impl = self._get_best_implementation(iface)
			if impl:
				debug("Will use implementation %s (version %s)", impl, impl.get_version())
				self.implementation[iface] = impl
				for d in impl.dependencies.values():
					debug("Considering dependency %s", d)
					process(self.get_interface(d.interface))
			else:
				debug("No implementation chould be chosen yet");
				self.ready = False
		process(self.get_interface(self.root))
		for w in self.watchers: w()

	# Only to be called from recalculate, as it is quite slow.
	# Use the results stored in self.implementation instead.
	def _get_best_implementation(self, iface):
		impls = iface.implementations.values()
		for f in self.usable_feeds(iface):
			debug("Processing feed %s", f)
			try:
				feed_iface = self.get_interface(f.uri)
				if feed_iface.name and iface.uri not in feed_iface.feed_for:
					warn("Missing <feed-for> for '%s' in '%s'",
						iface.uri, f.uri)
				if feed_iface.implementations:
					impls.extend(feed_iface.implementations.values())
			except NeedDownload, ex:
				raise ex
			except Exception, ex:
				warn("Failed to load feed %s for %s: %s",
					f, iface, str(ex))

		debug("get_best_implementation(%s), with feeds: %s", iface, iface.feeds)

		if not impls:
			info("Interface %s has no implementations!", iface)
			return None
		best = impls[0]
		for x in impls[1:]:
			if self.compare(iface, x, best) < 0:
				best = x
		if self.is_unusable(best):
			info("Best implementation of %s is %s, but unusable (%s)", iface, best,
							self.get_unusable_reason(best))
			return None
		return best

	def compare(self, interface, b, a):
		a_stab = a.get_stability()
		b_stab = b.get_stability()

		# Usable ones come first
		r = cmp(self.is_unusable(b), self.is_unusable(a))
		if r: return r

		# Preferred versions come first
		r = cmp(a_stab == preferred, b_stab == preferred)
		if r: return r

		if self.network_use != network_full:
			r = cmp(self.get_cached(a), self.get_cached(b))
			if r: return r

		# Stability
		stab_policy = interface.stability_policy
		if not stab_policy:
			if self.help_with_testing: stab_policy = testing
			else: stab_policy = stable

		if a_stab >= stab_policy: a_stab = preferred
		if b_stab >= stab_policy: b_stab = preferred

		r = cmp(a_stab, b_stab)
		if r: return r

		# Newer versions come before older ones
		r = cmp(a.version, b.version)
		if r: return r

		# Get best OS
		r = cmp(arch.os_ranks.get(a.os, None),
			arch.os_ranks.get(b.os, None))
		if r: return r

		# Get best machine
		r = cmp(arch.machine_ranks.get(a.machine, None),
			arch.machine_ranks.get(b.machine, None))
		if r: return r

		# Slightly prefer cached versions
		if self.network_use == network_full:
			r = cmp(self.get_cached(a), self.get_cached(b))
			if r: return r

		return cmp(a.id, b.id)

	def usable_feeds(self, iface):
		"""Generator for iface.feeds that are valid for our architecture."""
		for f in iface.feeds:
			if f.os in arch.os_ranks and f.machine in arch.machine_ranks:
				yield f
			else:
				debug("Skipping '%s'; unsupported architecture %s-%s",
					f, f.os, f.machine)

	def get_ranked_implementations(self, iface):
		impls = iface.implementations.values()
		for f in self.usable_feeds(iface):
			feed_iface = self.get_interface(f.uri)
			if feed_iface.implementations:
				impls.extend(feed_iface.implementations.values())
		impls.sort(lambda a, b: self.compare(iface, a, b))
		return impls

	def is_unusable(self, impl):
		return self.get_unusable_reason(impl) != None

	def get_unusable_reason(self, impl):
		"""Returns the reason why this impl is unusable, or None if it's OK"""
		stability = impl.get_stability()
		if stability <= buggy:
			return stability.name
		if self.network_use == network_offline and not self.get_cached(impl):
			return "Not cached and we are off-line"
		if impl.os not in arch.os_ranks:
			return "Unsupported OS"
		if impl.machine not in arch.machine_ranks:
			return "Unsupported machine type"
		return None

	def get_interface(self, uri):
		iface = iface_cache.get_interface(uri)

		if iface.last_modified is None:
			if self.network_use != network_offline:
				debug("Interface not cached and not off-line. Downloading...")
				self.begin_iface_download(iface)
			else:
				if self.warned_offline:
					debug("Nothing known about interface, but we are off-line.")
				else:
					warn("Nothing known about interface '%s', but we are in off-line mode "
						"(so not fetching)." % uri)
					self.warned_offline = True
		elif not uri.startswith('/'):
			staleness = time.time() - (iface.last_checked or 0)
			debug("Staleness for %s is %.2f hours", iface, staleness / 3600.0)

			if self.network_use != network_offline and self.freshness > 0 and staleness > self.freshness:
				debug("Updating %s", iface)
				self.begin_iface_download(iface, False)
		#else: debug("Local interface, so not checking staleness.")

		return iface

	def begin_iface_download(self, interface, force = False):
		debug("begin_iface_download %s (force = %d)", interface, force)
		if interface.uri.startswith('/'):
			return
		dl = download.begin_iface_download(interface, force)
		if not dl:
			assert not force
			debug("Already in progress")
			return

		debug("Need to download")
		# Calls update_interface_from_network eventually on success
		self.handler.monitor_download(dl)

	def get_implementation_path(self, impl):
		assert isinstance(impl, Implementation)
		if impl.id.startswith('/'):
			return impl.id
		return iface_cache.stores.lookup(impl.id)

	def get_implementation(self, interface):
		assert isinstance(interface, Interface)

		if not interface.name:
			raise SafeException("We don't have enough information to "
					    "run this program yet. "
					    "Need to download:\n%s" % interface.uri)
		try:
			return self.implementation[interface]
		except KeyError, ex:
			if interface.implementations:
				offline = ""
				if self.network_use == network_offline:
					offline = "\nThis may be because 'Network Use' is set to Off-line."
				raise SafeException("No usable implementation found for '%s'.%s" %
						(interface.name, offline))
			raise ex

	def walk_interfaces(self):
		# Deprecated
		return iter(self.implementation)

	def check_signed_data(self, download, signed_data):
		iface_cache.check_signed_data(download.interface, signed_data, self.handler)

	def get_cached(self, impl):
		if impl.id.startswith('/'):
			return os.path.exists(impl.id)
		else:
			try:
				path = self.get_implementation_path(impl)
				assert path
				return True
			except:
				pass # OK
		return False

	def add_to_cache(self, source, data):
		iface_cache.add_to_cache(source, data)

	def get_uncached_implementations(self):
		uncached = []
		for iface in self.implementation:
			impl = self.implementation[iface]
			assert impl
			if not self.get_cached(impl):
				uncached.append((iface, impl))
		return uncached

	def refresh_all(self, force = True):
		for x in self.walk_interfaces():
			self.begin_iface_download(x, force)
			for f in self.usable_feeds(x):
				feed_iface = self.get_interface(f.uri)
				self.begin_iface_download(feed_iface, force)

	def interface_changed(self, interface):
		debug("interface_changed(%s): recalculating", interface)
		self.recalculate()

	def get_feed_targets(self, feed_iface_uri):
		"""Return a list of Interfaces for which feed_iface can be a feed.
		This is used by --feed. If there are no interfaces, raises SafeException."""
		feed_iface = self.get_interface(feed_iface_uri)
		if not feed_iface.feed_for:
			if not feed_iface.name:
				raise SafeException("Can't get feed targets for '%s'; failed to load interface." %
						feed_iface_uri)
			raise SafeException("Missing <feed-for> element in '%s'; "
					"this interface can't be used as a feed." % feed_iface_uri)
		feed_targets = feed_iface.feed_for
		if not feed_iface.name:
			warn("Warning: unknown interface '%s'" % feed_iface_uri)
		return [self.get_interface(uri) for uri in feed_targets]
<|endoftext|>


<commit_before>import sys
import os
import shutil

import unittest
import tarfile

from test import test_support

# Check for our compression modules.
try:
    import gzip
    gzip.GzipFile
except (ImportError, AttributeError):
    gzip = None
try:
    import bz2
except ImportError:
    bz2 = None

def path(path):
    return test_support.findfile(path)

testtar = path("testtar" + os.extsep + "tar")
tempdir = path("testtar" + os.extsep + "dir")
tempname = path("testtar" + os.extsep + "tmp")
membercount = 10

def tarname(comp=""):
    if not comp:
        return testtar
    return "%s%s%s" % (testtar, os.extsep, comp)

def dirname():
    if not os.path.exists(tempdir):
        os.mkdir(tempdir)
    return tempdir

def tmpname():
    return tempname

class BaseTest(unittest.TestCase):
    comp = ''
    mode = 'r'
    sep = ':'

    def setUp(self):
        mode = self.mode + self.sep + self.comp
        self.tar = tarfile.open(tarname(self.comp), mode)

    def tearDown(self):
        self.tar.close()

class ReadTest(BaseTest):

    def test(self):
        """Test member extraction.
        """
        members = 0
        for tarinfo in self.tar:
            members += 1
            if not tarinfo.isreg():
                continue
            f = self.tar.extractfile(tarinfo)
            self.assert_(len(f.read()) == tarinfo.size,
                         "size read does not match expected size")
            f.close()

        self.assert_(members == membercount,
                     "could not find all members")

    def test_sparse(self):
        """Test sparse member extraction.
        """
        if self.sep != "|":
            f1 = self.tar.extractfile("S-SPARSE")
            f2 = self.tar.extractfile("S-SPARSE-WITH-NULLS")
            self.assert_(f1.read() == f2.read(),
                         "_FileObject failed on sparse file member")

    def test_readlines(self):
        """Test readlines() method of _FileObject.
        """
        if self.sep != "|":
            filename = "0-REGTYPE-TEXT"
            self.tar.extract(filename, dirname())
            lines1 = file(os.path.join(dirname(), filename), "rU").readlines()
            lines2 = self.tar.extractfile(filename).readlines()
            self.assert_(lines1 == lines2,
                         "_FileObject.readline() does not work correctly")

    def test_seek(self):
        """Test seek() method of _FileObject, incl. random reading.
        """
        if self.sep != "|":
            filename = "0-REGTYPE"
            self.tar.extract(filename, dirname())
            data = file(os.path.join(dirname(), filename), "rb").read()

            tarinfo = self.tar.getmember(filename)
            fobj = self.tar.extractfile(tarinfo)

            text = fobj.read()
            fobj.seek(0)
            self.assert_(0 == fobj.tell(),
                         "seek() to file's start failed")
            fobj.seek(2048, 0)
            self.assert_(2048 == fobj.tell(),
                         "seek() to absolute position failed")
            fobj.seek(-1024, 1)
            self.assert_(1024 == fobj.tell(),
                         "seek() to negative relative position failed")
            fobj.seek(1024, 1)
            self.assert_(2048 == fobj.tell(),
                         "seek() to positive relative position failed")
            s = fobj.read(10)
            self.assert_(s == data[2048:2058],
                         "read() after seek failed")
            fobj.seek(0, 2)
            self.assert_(tarinfo.size == fobj.tell(),
                         "seek() to file's end failed")
            self.assert_(fobj.read() == "",
                         "read() at file's end did not return empty string")
            fobj.seek(-tarinfo.size, 2)
            self.assert_(0 == fobj.tell(),
                         "relative seek() to file's start failed")
            fobj.seek(512)
            s1 = fobj.readlines()
            fobj.seek(512)
            s2 = fobj.readlines()
            self.assert_(s1 == s2,
                         "readlines() after seek failed")
            fobj.close()

class ReadStreamTest(ReadTest):
    sep = "|"

    def test(self):
        """Test member extraction, and for StreamError when
           seeking backwards.
        """
        ReadTest.test(self)
        tarinfo = self.tar.getmembers()[0]
        f = self.tar.extractfile(tarinfo)
        self.assertRaises(tarfile.StreamError, f.read)

    def test_stream(self):
        """Compare the normal tar and the stream tar.
        """
        stream = self.tar
        tar = tarfile.open(tarname(), 'r')

        while 1:
            t1 = tar.next()
            t2 = stream.next()
            if t1 is None:
                break
            self.assert_(t2 is not None, "stream.next() failed.")

            if t2.islnk() or t2.issym():
                self.assertRaises(tarfile.StreamError, stream.extractfile, t2)
                continue
            v1 = tar.extractfile(t1)
            v2 = stream.extractfile(t2)
            if v1 is None:
                continue
            self.assert_(v2 is not None, "stream.extractfile() failed")
            self.assert_(v1.read() == v2.read(), "stream extraction failed")

        stream.close()

class WriteTest(BaseTest):
    mode = 'w'

    def setUp(self):
        mode = self.mode + self.sep + self.comp
        self.src = tarfile.open(tarname(self.comp), 'r')
        self.dst = tarfile.open(tmpname(), mode)

    def tearDown(self):
        self.src.close()
        self.dst.close()

    def test_posix(self):
        self.dst.posix = 1
        self._test()

    def test_nonposix(self):
        self.dst.posix = 0
        self._test()

    def _test(self):
        for tarinfo in self.src:
            if not tarinfo.isreg():
                continue
            f = self.src.extractfile(tarinfo)
            if self.dst.posix and len(tarinfo.name) > tarfile.LENGTH_NAME:
                self.assertRaises(ValueError, self.dst.addfile,
                                 tarinfo, f)
            else:
                self.dst.addfile(tarinfo, f)

class WriteStreamTest(WriteTest):
    sep = '|'

# Gzip TestCases
class ReadTestGzip(ReadTest):
    comp = "gz"
class ReadStreamTestGzip(ReadStreamTest):
    comp = "gz"
class WriteTestGzip(WriteTest):
    comp = "gz"
class WriteStreamTestGzip(WriteStreamTest):
    comp = "gz"

if bz2:
    # Bzip2 TestCases
    class ReadTestBzip2(ReadTestGzip):
        comp = "bz2"
    class ReadStreamTestBzip2(ReadStreamTestGzip):
        comp = "bz2"
    class WriteTestBzip2(WriteTest):
        comp = "bz2"
    class WriteStreamTestBzip2(WriteStreamTestGzip):
        comp = "bz2"

# If importing gzip failed, discard the Gzip TestCases.
if not gzip:
    del ReadTestGzip
    del ReadStreamTestGzip
    del WriteTestGzip
    del WriteStreamTestGzip

def test_main():
    if gzip:
        # create testtar.tar.gz
        gzip.open(tarname("gz"), "wb").write(file(tarname(), "rb").read())
    if bz2:
        # create testtar.tar.bz2
        bz2.BZ2File(tarname("bz2"), "wb").write(file(tarname(), "rb").read())

    tests = [
        ReadTest,
        ReadStreamTest,
        WriteTest,
        WriteStreamTest
    ]

    if gzip:
        tests.extend([
            ReadTestGzip, ReadStreamTestGzip,
            WriteTestGzip, WriteStreamTestGzip
        ])

    if bz2:
        tests.extend([
            ReadTestBzip2, ReadStreamTestBzip2,
            WriteTestBzip2, WriteStreamTestBzip2
        ])
    try:
        test_support.run_unittest(*tests)
    finally:
        if gzip:
            os.remove(tarname("gz"))
        if bz2:
            os.remove(tarname("bz2"))
        if os.path.exists(tempdir):
            shutil.rmtree(tempdir)
        if os.path.exists(tempname):
            os.remove(tempname)

if __name__ == "__main__":
    test_main()
<commit_msg>Make creation of temporary file and directory more portable.  Also made cleanup code use proper functions to get paths.<commit_after>import sys
import os
import shutil
import tempfile

import unittest
import tarfile

from test import test_support

# Check for our compression modules.
try:
    import gzip
    gzip.GzipFile
except (ImportError, AttributeError):
    gzip = None
try:
    import bz2
except ImportError:
    bz2 = None

def path(path):
    return test_support.findfile(path)

testtar = path("testtar.tar")
tempdir = os.path.join(tempfile.gettempdir(), "testtar" + os.extsep + "dir")
tempname = test_support.TESTFN
membercount = 10

def tarname(comp=""):
    if not comp:
        return testtar
    return "%s%s%s" % (testtar, os.extsep, comp)

def dirname():
    if not os.path.exists(tempdir):
        os.mkdir(tempdir)
    return tempdir

def tmpname():
    return tempname

class BaseTest(unittest.TestCase):
    comp = ''
    mode = 'r'
    sep = ':'

    def setUp(self):
        mode = self.mode + self.sep + self.comp
        self.tar = tarfile.open(tarname(self.comp), mode)

    def tearDown(self):
        self.tar.close()

class ReadTest(BaseTest):

    def test(self):
        """Test member extraction.
        """
        members = 0
        for tarinfo in self.tar:
            members += 1
            if not tarinfo.isreg():
                continue
            f = self.tar.extractfile(tarinfo)
            self.assert_(len(f.read()) == tarinfo.size,
                         "size read does not match expected size")
            f.close()

        self.assert_(members == membercount,
                     "could not find all members")

    def test_sparse(self):
        """Test sparse member extraction.
        """
        if self.sep != "|":
            f1 = self.tar.extractfile("S-SPARSE")
            f2 = self.tar.extractfile("S-SPARSE-WITH-NULLS")
            self.assert_(f1.read() == f2.read(),
                         "_FileObject failed on sparse file member")

    def test_readlines(self):
        """Test readlines() method of _FileObject.
        """
        if self.sep != "|":
            filename = "0-REGTYPE-TEXT"
            self.tar.extract(filename, dirname())
            lines1 = file(os.path.join(dirname(), filename), "rU").readlines()
            lines2 = self.tar.extractfile(filename).readlines()
            self.assert_(lines1 == lines2,
                         "_FileObject.readline() does not work correctly")

    def test_seek(self):
        """Test seek() method of _FileObject, incl. random reading.
        """
        if self.sep != "|":
            filename = "0-REGTYPE"
            self.tar.extract(filename, dirname())
            data = file(os.path.join(dirname(), filename), "rb").read()

            tarinfo = self.tar.getmember(filename)
            fobj = self.tar.extractfile(tarinfo)

            text = fobj.read()
            fobj.seek(0)
            self.assert_(0 == fobj.tell(),
                         "seek() to file's start failed")
            fobj.seek(2048, 0)
            self.assert_(2048 == fobj.tell(),
                         "seek() to absolute position failed")
            fobj.seek(-1024, 1)
            self.assert_(1024 == fobj.tell(),
                         "seek() to negative relative position failed")
            fobj.seek(1024, 1)
            self.assert_(2048 == fobj.tell(),
                         "seek() to positive relative position failed")
            s = fobj.read(10)
            self.assert_(s == data[2048:2058],
                         "read() after seek failed")
            fobj.seek(0, 2)
            self.assert_(tarinfo.size == fobj.tell(),
                         "seek() to file's end failed")
            self.assert_(fobj.read() == "",
                         "read() at file's end did not return empty string")
            fobj.seek(-tarinfo.size, 2)
            self.assert_(0 == fobj.tell(),
                         "relative seek() to file's start failed")
            fobj.seek(512)
            s1 = fobj.readlines()
            fobj.seek(512)
            s2 = fobj.readlines()
            self.assert_(s1 == s2,
                         "readlines() after seek failed")
            fobj.close()

class ReadStreamTest(ReadTest):
    sep = "|"

    def test(self):
        """Test member extraction, and for StreamError when
           seeking backwards.
        """
        ReadTest.test(self)
        tarinfo = self.tar.getmembers()[0]
        f = self.tar.extractfile(tarinfo)
        self.assertRaises(tarfile.StreamError, f.read)

    def test_stream(self):
        """Compare the normal tar and the stream tar.
        """
        stream = self.tar
        tar = tarfile.open(tarname(), 'r')

        while 1:
            t1 = tar.next()
            t2 = stream.next()
            if t1 is None:
                break
            self.assert_(t2 is not None, "stream.next() failed.")

            if t2.islnk() or t2.issym():
                self.assertRaises(tarfile.StreamError, stream.extractfile, t2)
                continue
            v1 = tar.extractfile(t1)
            v2 = stream.extractfile(t2)
            if v1 is None:
                continue
            self.assert_(v2 is not None, "stream.extractfile() failed")
            self.assert_(v1.read() == v2.read(), "stream extraction failed")

        stream.close()

class WriteTest(BaseTest):
    mode = 'w'

    def setUp(self):
        mode = self.mode + self.sep + self.comp
        self.src = tarfile.open(tarname(self.comp), 'r')
        self.dst = tarfile.open(tmpname(), mode)

    def tearDown(self):
        self.src.close()
        self.dst.close()

    def test_posix(self):
        self.dst.posix = 1
        self._test()

    def test_nonposix(self):
        self.dst.posix = 0
        self._test()

    def _test(self):
        for tarinfo in self.src:
            if not tarinfo.isreg():
                continue
            f = self.src.extractfile(tarinfo)
            if self.dst.posix and len(tarinfo.name) > tarfile.LENGTH_NAME:
                self.assertRaises(ValueError, self.dst.addfile,
                                 tarinfo, f)
            else:
                self.dst.addfile(tarinfo, f)

class WriteStreamTest(WriteTest):
    sep = '|'

# Gzip TestCases
class ReadTestGzip(ReadTest):
    comp = "gz"
class ReadStreamTestGzip(ReadStreamTest):
    comp = "gz"
class WriteTestGzip(WriteTest):
    comp = "gz"
class WriteStreamTestGzip(WriteStreamTest):
    comp = "gz"

if bz2:
    # Bzip2 TestCases
    class ReadTestBzip2(ReadTestGzip):
        comp = "bz2"
    class ReadStreamTestBzip2(ReadStreamTestGzip):
        comp = "bz2"
    class WriteTestBzip2(WriteTest):
        comp = "bz2"
    class WriteStreamTestBzip2(WriteStreamTestGzip):
        comp = "bz2"

# If importing gzip failed, discard the Gzip TestCases.
if not gzip:
    del ReadTestGzip
    del ReadStreamTestGzip
    del WriteTestGzip
    del WriteStreamTestGzip

def test_main():
    if gzip:
        # create testtar.tar.gz
        gzip.open(tarname("gz"), "wb").write(file(tarname(), "rb").read())
    if bz2:
        # create testtar.tar.bz2
        bz2.BZ2File(tarname("bz2"), "wb").write(file(tarname(), "rb").read())

    tests = [
        ReadTest,
        ReadStreamTest,
        WriteTest,
        WriteStreamTest
    ]

    if gzip:
        tests.extend([
            ReadTestGzip, ReadStreamTestGzip,
            WriteTestGzip, WriteStreamTestGzip
        ])

    if bz2:
        tests.extend([
            ReadTestBzip2, ReadStreamTestBzip2,
            WriteTestBzip2, WriteStreamTestBzip2
        ])
    try:
        test_support.run_unittest(*tests)
    finally:
        if gzip:
            os.remove(tarname("gz"))
        if bz2:
            os.remove(tarname("bz2"))
        if os.path.exists(dirname()):
            shutil.rmtree(dirname())
        if os.path.exists(tmpname()):
            os.remove(tmpname())

if __name__ == "__main__":
    test_main()
<|endoftext|>


<commit_before>import os
import shutil
from io import StringIO, BytesIO, open
import zipfile

haveFS = False
try:
	import fs
	from fs.osfs import OSFS
	from fs.zipfs import ZipFS, ZipOpenError
	haveFS = True
except ImportError:
	pass

from ufoLib.plistlib import readPlist, writePlist
from ufoLib.errors import UFOLibError

try:
	basestring
except NameError:
	basestring = str

def sniffFileStructure(path):
	if zipfile.is_zipfile(path):
		return "zip"
	elif os.path.isdir(path):
		return "package"
	raise UFOLibError("The specified UFO does not have a known structure.")

class FileSystem(object):

	def __init__(self, path, mode="r", structure=None):
		"""
		path can be a path or a fs file system object.

		mode can be r or w.

		structure is only applicable in "w" mode. Options:
		None: existing structure
		package: package structure
		zip: zipped package

		mode and structure are both ignored if a
		fs file system object is given for path.
		"""
		self._root = None
		self._path = "<data stream>"
		if isinstance(path, basestring):
			self._path = path
			if mode == "w":
				if os.path.exists(path):
					existingStructure = sniffFileStructure(path)
					if structure is not None:
						if structure != existingStructure:
							raise UFOLibError("A UFO with a different structure already exists at the given path.")
					else:
						structure = existingStructure
			elif mode == "r":
				if not os.path.exists(path):
					raise UFOLibError("The specified UFO doesn't exist.")
				structure = sniffFileStructure(path)
			if structure == "package":
				if mode == "w" and not os.path.exists(path):
					os.mkdir(path)
				path = OSFS(path)
			elif structure == "zip":
				if not haveFS:
					raise UFOLibError("The fs module is required for reading and writing UFO ZIP.")
				path = ZipFS(path, mode=mode, allow_zip_64=True, encoding="utf8")
				roots = path.listdir("")
				if not roots:
					self._root = "contents"
				elif len(roots) > 1:
					raise UFOLibError("The UFO contains more than one root.")
				else:
					self._root = roots[0]
		self._fs = path

	def close(self):
		self._fsClose()

	# --------
	# fs Calls
	# --------

	"""
	All actual low-level file system interaction
	MUST be done through these methods.

	This is necessary because ZIPs will have a
	top level root directory that packages will
	not have.
	"""

	def _fsRootPath(self, path):
		if self._root is None:
			return path
		return self.joinPath(self._root, path)

	def _fsUnRootPath(self, path):
		if self._root is None:
			return path
		return self.relativePath(path, self._root)

	def _fsClose(self):
		self._fs.close()

	def _fsOpen(self, path, mode="r", encoding=None):
		path = self._fsRootPath(path)
		f = self._fs.open(path, mode, encoding=encoding)
		return f

	def _fsRemove(self, path):
		path = self._fsRootPath(path)
		self._fs.remove(path)

	def _fsMakeDirectory(self, path):
		path = self._fsRootPath(path)
		self._fs.makedir(path)

	def _fsRemoveDirectory(self, path):
		path = self._fsRootPath(path)
		self._fs.removedir(path)

	def _fsMove(self, path1, path2):
		if self.isDirectory(path1):
			meth = self._fs.movedir
		else:
			meth = self._fs.move
		path1 = self._fsRootPath(path1)
		path2 = self._fsRootPath(path2)
		meth(path1, path2)

	def _fsExists(self, path):
		path = self._fsRootPath(path)
		return self._fs.exists(path)

	def _fsIsDirectory(self, path):
		path = self._fsRootPath(path)
		return self._fs.isdir(path)

	def _fsListDirectory(self, path):
		path = self._fsRootPath(path)
		return self._fs.listdir(path)

	def _fsGetFileModificationTime(self, path):
		path = self._fsRootPath(path)
		info = self._fs.getinfo(path)
		return info["modified_time"]

	# -----------------
	# Path Manipulation
	# -----------------

	def joinPath(self, *parts):
		return fs.path.join(*parts)

	def splitPath(self, path):
		return fs.path.split(path)

	def directoryName(self, path):
		return fs.path.dirname(path)

	def relativePath(self, path, start):
		return fs.relativefrom(path, start)

	# ---------
	# Existence
	# ---------

	def exists(self, path):
		return self._fsExists(path)

	def isDirectory(self, path):
		return self._fsIsDirectory(path)

	def listDirectory(self, path, recurse=False):
		return self._listDirectory(path, recurse=recurse)

	def _listDirectory(self, path, recurse=False, depth=0, maxDepth=100):
		if depth > maxDepth:
			raise UFOLibError("Maximum recusion depth reached.")
		result = []
		for fileName in self._fsListDirectory(path):
			p = self.joinPath(path, fileName)
			if self.isDirectory(p) and recurse:
				result += self._listDirectory(p, recurse=True, depth=depth+1, maxDepth=maxDepth)
			else:
				result.append(p)
		return result

	def makeDirectory(self, path):
		if not self.exists(path):
			self._fsMakeDirectory(path)

	# -----------
	# File Opener
	# -----------

	def open(self, path, mode="r", encoding=None):
		"""
		Returns a file (or file-like) object for the
		file at the given path. The path must be relative
		to the UFO path. Returns None if the file does
		not exist and the mode is "r" or "rb. An encoding
		may be passed if needed.

		Note: The caller is responsible for closing the open file.
		"""
		if encoding:
			if mode == "r":
				mode = "rb"
			elif mode == "w":
				mode = "wb"
		if mode in ("r", "rb") and not self.exists(path):
			return None
		if self.exists(path) and self.isDirectory(path):
			raise UFOLibError("%s is a directory." % path)
		if mode in ("w", "wb"):
			self._buildDirectoryTree(path)
		f = self._fsOpen(path, mode, encoding=encoding)
		return f

	def _buildDirectoryTree(self, path):
		directory, fileName = self.splitPath(path)
		directoryTree = []
		while directory:
			directory, d = self.splitPath(directory)
			directoryTree.append(d)
		directoryTree.reverse()
		built = ""
		for d in directoryTree:
			d = self.joinPath(built, d)
			self.makeDirectory(d)
			built = d

	# ------------------
	# Modification Times
	# ------------------

	def getFileModificationTime(self, path):
		"""
		Returns the modification time (as reported by os.path.getmtime)
		for the file at the given path. The path must be relative to
		the UFO path. Returns None if the file does not exist.
		"""
		if not self.exists(path):
			return None
		return self._fsGetFileModificationTime(path)

	# --------------
	# Raw Read/Write
	# --------------

	def readBytesFromPath(self, path, encoding=None):
		"""
		Returns the bytes in the file at the given path.
		The path must be relative to the UFO path.
		Returns None if the file does not exist.
		An encoding may be passed if needed.
		"""
		f = self.open(path, mode="r", encoding=encoding)
		if f is None:
			return None
		data = f.read()
		f.close()
		return data

	def writeBytesToPath(self, path, data, encoding=None):
		"""
		Write bytes to path. If needed, the directory tree
		for the given path will be built. The path must be
		relative to the UFO. An encoding may be passed if needed.
		"""
		if encoding:
			data = StringIO(data).encode(encoding)
		self._writeFileAtomically(data, path)

	def _writeFileAtomically(self, data, path):
		"""
		Write data into a file at path. Do this sort of atomically
		making it harder to cause corrupt files. This also checks to see
		if data matches the data that is already in the file at path.
		If so, the file is not rewritten so that the modification date
		is preserved.
		"""
		assert isinstance(data, bytes)
		if self.exists(path):
			f = self.open(path, "rb")
			oldData = f.read()
			f.close()
			if data == oldData:
				return
		if data:
			f = self.open(path, "wb")
			f.write(data)
			f.close()

	# ------------
	# File Removal
	# ------------

	def remove(self, path):
		"""
		Remove the file (or directory) at path. The path
		must be relative to the UFO.
		"""
		if not self.exists(path):
			return
		self._removeFileForPath(path, raiseErrorIfMissing=True)

	def _removeFileForPath(self, path, raiseErrorIfMissing=False):
		if not self.exists(path):
			if raiseErrorIfMissing:
				raise UFOLibError("The file %s does not exist." % path)
		else:
			if self.isDirectory(path):
				self._fsRemoveDirectory(path)
			else:
				self._fsRemove(path)
		directory = self.directoryName(path)
		self._removeEmptyDirectoriesForPath(directory)

	def _removeEmptyDirectoriesForPath(self, directory):
		if not self.exists(directory):
			return
		if not len(self._fsListDirectory(directory)):
			self._fsRemoveDirectory(directory)
		else:
			return
		directory = self.directoryName(directory)
		if directory:
			self._removeEmptyDirectoriesForPath(directory)

	# ----
	# Move
	# ----

	def move(self, path1, path2):
		if not self.exists(path1):
			raise UFOLibError("%s does not exist." % path1)
		if self.exists(path2):
			raise UFOLibError("%s already exists." % path2)
		self._fsMove(path1, path2)

	# --------------
	# Property Lists
	# --------------

	def readPlist(self, path, default=None):
		"""
		Read a property list relative to the
		UFO path. If the file is missing and
		default is None a UFOLibError will be
		raised. Otherwise default is returned.
		The errors that could be raised during
		the reading of a plist are unpredictable
		and/or too large to list, so, a blind
		try: except: is done. If an exception
		occurs, a UFOLibError will be raised.
		"""
		if not self.exists(path):
			if default is not None:
				return default
			else:
				raise UFOLibError("%s is missing. This file is required" % path)
		try:
			with self.open(path, "rb") as f:
				return readPlist(f)
		except:
			raise UFOLibError("The file %s could not be read." % path)

	def writePlist(self, path, obj):
		"""
		Write a property list.

		Do this sort of atomically, making it harder to
		cause corrupt files, for example when writePlist
		encounters an error halfway during write. This
		also checks to see if text matches the text that
		is already in the file at path. If so, the file
		is not rewritten so that the modification date
		is preserved.

		The errors that could be raised during the writing
		of a plist are unpredictable and/or too large to list,
		so, a blind try: except: is done. If an exception occurs,
		a UFOLibError will be raised.
		"""
		try:
			f = BytesIO()
			writePlist(obj, f)
			data = f.getvalue()
		except:
			raise UFOLibError("The data for the file %s could not be written because it is not properly formatted." % path)
		self.writeBytesToPath(path, data)

class _NOFS(object):

	def __init__(self, path):
		self._path = path

	def _absPath(self, path):
		return os.path.join(self._path, path)

	def joinPath(self, *parts):
		return os.path.join(*parts)

	def splitPath(self, path):
		return os.path.split(path)

	def directoryName(self, path):
		return os.path.split(path)[0]

	def relativePath(self, path, start):
		return os.path.relpath(path, start)

	def close(self):
		pass

	def open(self, path, mode, encoding=None):
		path = self._absPath(path)
		return open(path, mode=mode, encoding=encoding)

	def remove(self, path):
		path = self._absPath(path)
		os.remove(path)

	def makedir(self, path):
		path = self._absPath(path)
		os.mkdir(path)

	def removedir(self, path):
		path = self._absPath(path)
		shutil.rmtree(path)

	def move(self, path1, path2):
		path1 = self._absPath(path1)
		path2 = self._absPath(path2)
		os.move(path1, path2)

	def movedir(self, path1, path2):
		path1 = self._absPath(path1)
		path2 = self._absPath(path2)
		shutil.move(path1, path2)

	def exists(self, path):
		path = self._absPath(path)
		return os.path.exists(path)

	def isdir(self, path):
		path = self._absPath(path)
		return os.path.isdir(path)

	def listdir(self, path):
		path = self._absPath(path)
		return os.listdir(path)

	def getinfo(self, path):
		path = self._absPath(path)
		stat = os.stat(path)
		info = dict(
			modified_time=stat.st_mtime
		)
		return info

if not haveFS:
	OSFS = _NOFS
<commit_msg>Switch to rb mode.<commit_after>import os
import shutil
from io import StringIO, BytesIO, open
import zipfile

haveFS = False
try:
	import fs
	from fs.osfs import OSFS
	from fs.zipfs import ZipFS, ZipOpenError
	haveFS = True
except ImportError:
	pass

from ufoLib.plistlib import readPlist, writePlist
from ufoLib.errors import UFOLibError

try:
	basestring
except NameError:
	basestring = str

def sniffFileStructure(path):
	if zipfile.is_zipfile(path):
		return "zip"
	elif os.path.isdir(path):
		return "package"
	raise UFOLibError("The specified UFO does not have a known structure.")

class FileSystem(object):

	def __init__(self, path, mode="r", structure=None):
		"""
		path can be a path or a fs file system object.

		mode can be r or w.

		structure is only applicable in "w" mode. Options:
		None: existing structure
		package: package structure
		zip: zipped package

		mode and structure are both ignored if a
		fs file system object is given for path.
		"""
		self._root = None
		self._path = "<data stream>"
		if isinstance(path, basestring):
			self._path = path
			if mode == "w":
				if os.path.exists(path):
					existingStructure = sniffFileStructure(path)
					if structure is not None:
						if structure != existingStructure:
							raise UFOLibError("A UFO with a different structure already exists at the given path.")
					else:
						structure = existingStructure
			elif mode == "r":
				if not os.path.exists(path):
					raise UFOLibError("The specified UFO doesn't exist.")
				structure = sniffFileStructure(path)
			if structure == "package":
				if mode == "w" and not os.path.exists(path):
					os.mkdir(path)
				path = OSFS(path)
			elif structure == "zip":
				if not haveFS:
					raise UFOLibError("The fs module is required for reading and writing UFO ZIP.")
				path = ZipFS(path, mode=mode, allow_zip_64=True, encoding="utf8")
				roots = path.listdir("")
				if not roots:
					self._root = "contents"
				elif len(roots) > 1:
					raise UFOLibError("The UFO contains more than one root.")
				else:
					self._root = roots[0]
		self._fs = path

	def close(self):
		self._fsClose()

	# --------
	# fs Calls
	# --------

	"""
	All actual low-level file system interaction
	MUST be done through these methods.

	This is necessary because ZIPs will have a
	top level root directory that packages will
	not have.
	"""

	def _fsRootPath(self, path):
		if self._root is None:
			return path
		return self.joinPath(self._root, path)

	def _fsUnRootPath(self, path):
		if self._root is None:
			return path
		return self.relativePath(path, self._root)

	def _fsClose(self):
		self._fs.close()

	def _fsOpen(self, path, mode="r", encoding=None):
		path = self._fsRootPath(path)
		f = self._fs.open(path, mode, encoding=encoding)
		return f

	def _fsRemove(self, path):
		path = self._fsRootPath(path)
		self._fs.remove(path)

	def _fsMakeDirectory(self, path):
		path = self._fsRootPath(path)
		self._fs.makedir(path)

	def _fsRemoveDirectory(self, path):
		path = self._fsRootPath(path)
		self._fs.removedir(path)

	def _fsMove(self, path1, path2):
		if self.isDirectory(path1):
			meth = self._fs.movedir
		else:
			meth = self._fs.move
		path1 = self._fsRootPath(path1)
		path2 = self._fsRootPath(path2)
		meth(path1, path2)

	def _fsExists(self, path):
		path = self._fsRootPath(path)
		return self._fs.exists(path)

	def _fsIsDirectory(self, path):
		path = self._fsRootPath(path)
		return self._fs.isdir(path)

	def _fsListDirectory(self, path):
		path = self._fsRootPath(path)
		return self._fs.listdir(path)

	def _fsGetFileModificationTime(self, path):
		path = self._fsRootPath(path)
		info = self._fs.getinfo(path)
		return info["modified_time"]

	# -----------------
	# Path Manipulation
	# -----------------

	def joinPath(self, *parts):
		return fs.path.join(*parts)

	def splitPath(self, path):
		return fs.path.split(path)

	def directoryName(self, path):
		return fs.path.dirname(path)

	def relativePath(self, path, start):
		return fs.relativefrom(path, start)

	# ---------
	# Existence
	# ---------

	def exists(self, path):
		return self._fsExists(path)

	def isDirectory(self, path):
		return self._fsIsDirectory(path)

	def listDirectory(self, path, recurse=False):
		return self._listDirectory(path, recurse=recurse)

	def _listDirectory(self, path, recurse=False, depth=0, maxDepth=100):
		if depth > maxDepth:
			raise UFOLibError("Maximum recusion depth reached.")
		result = []
		for fileName in self._fsListDirectory(path):
			p = self.joinPath(path, fileName)
			if self.isDirectory(p) and recurse:
				result += self._listDirectory(p, recurse=True, depth=depth+1, maxDepth=maxDepth)
			else:
				result.append(p)
		return result

	def makeDirectory(self, path):
		if not self.exists(path):
			self._fsMakeDirectory(path)

	# -----------
	# File Opener
	# -----------

	def open(self, path, mode="r", encoding=None):
		"""
		Returns a file (or file-like) object for the
		file at the given path. The path must be relative
		to the UFO path. Returns None if the file does
		not exist and the mode is "r" or "rb. An encoding
		may be passed if needed.

		Note: The caller is responsible for closing the open file.
		"""
		if encoding:
			if mode == "r":
				mode = "rb"
			elif mode == "w":
				mode = "wb"
		if mode in ("r", "rb") and not self.exists(path):
			return None
		if self.exists(path) and self.isDirectory(path):
			raise UFOLibError("%s is a directory." % path)
		if mode in ("w", "wb"):
			self._buildDirectoryTree(path)
		f = self._fsOpen(path, mode, encoding=encoding)
		return f

	def _buildDirectoryTree(self, path):
		directory, fileName = self.splitPath(path)
		directoryTree = []
		while directory:
			directory, d = self.splitPath(directory)
			directoryTree.append(d)
		directoryTree.reverse()
		built = ""
		for d in directoryTree:
			d = self.joinPath(built, d)
			self.makeDirectory(d)
			built = d

	# ------------------
	# Modification Times
	# ------------------

	def getFileModificationTime(self, path):
		"""
		Returns the modification time (as reported by os.path.getmtime)
		for the file at the given path. The path must be relative to
		the UFO path. Returns None if the file does not exist.
		"""
		if not self.exists(path):
			return None
		return self._fsGetFileModificationTime(path)

	# --------------
	# Raw Read/Write
	# --------------

	def readBytesFromPath(self, path, encoding=None):
		"""
		Returns the bytes in the file at the given path.
		The path must be relative to the UFO path.
		Returns None if the file does not exist.
		An encoding may be passed if needed.
		"""
		f = self.open(path, mode="rb", encoding=encoding)
		if f is None:
			return None
		data = f.read()
		f.close()
		return data

	def writeBytesToPath(self, path, data, encoding=None):
		"""
		Write bytes to path. If needed, the directory tree
		for the given path will be built. The path must be
		relative to the UFO. An encoding may be passed if needed.
		"""
		if encoding:
			data = StringIO(data).encode(encoding)
		self._writeFileAtomically(data, path)

	def _writeFileAtomically(self, data, path):
		"""
		Write data into a file at path. Do this sort of atomically
		making it harder to cause corrupt files. This also checks to see
		if data matches the data that is already in the file at path.
		If so, the file is not rewritten so that the modification date
		is preserved.
		"""
		assert isinstance(data, bytes)
		if self.exists(path):
			f = self.open(path, "rb")
			oldData = f.read()
			f.close()
			if data == oldData:
				return
		if data:
			f = self.open(path, "wb")
			f.write(data)
			f.close()

	# ------------
	# File Removal
	# ------------

	def remove(self, path):
		"""
		Remove the file (or directory) at path. The path
		must be relative to the UFO.
		"""
		if not self.exists(path):
			return
		self._removeFileForPath(path, raiseErrorIfMissing=True)

	def _removeFileForPath(self, path, raiseErrorIfMissing=False):
		if not self.exists(path):
			if raiseErrorIfMissing:
				raise UFOLibError("The file %s does not exist." % path)
		else:
			if self.isDirectory(path):
				self._fsRemoveDirectory(path)
			else:
				self._fsRemove(path)
		directory = self.directoryName(path)
		self._removeEmptyDirectoriesForPath(directory)

	def _removeEmptyDirectoriesForPath(self, directory):
		if not self.exists(directory):
			return
		if not len(self._fsListDirectory(directory)):
			self._fsRemoveDirectory(directory)
		else:
			return
		directory = self.directoryName(directory)
		if directory:
			self._removeEmptyDirectoriesForPath(directory)

	# ----
	# Move
	# ----

	def move(self, path1, path2):
		if not self.exists(path1):
			raise UFOLibError("%s does not exist." % path1)
		if self.exists(path2):
			raise UFOLibError("%s already exists." % path2)
		self._fsMove(path1, path2)

	# --------------
	# Property Lists
	# --------------

	def readPlist(self, path, default=None):
		"""
		Read a property list relative to the
		UFO path. If the file is missing and
		default is None a UFOLibError will be
		raised. Otherwise default is returned.
		The errors that could be raised during
		the reading of a plist are unpredictable
		and/or too large to list, so, a blind
		try: except: is done. If an exception
		occurs, a UFOLibError will be raised.
		"""
		if not self.exists(path):
			if default is not None:
				return default
			else:
				raise UFOLibError("%s is missing. This file is required" % path)
		try:
			with self.open(path, "rb") as f:
				return readPlist(f)
		except:
			raise UFOLibError("The file %s could not be read." % path)

	def writePlist(self, path, obj):
		"""
		Write a property list.

		Do this sort of atomically, making it harder to
		cause corrupt files, for example when writePlist
		encounters an error halfway during write. This
		also checks to see if text matches the text that
		is already in the file at path. If so, the file
		is not rewritten so that the modification date
		is preserved.

		The errors that could be raised during the writing
		of a plist are unpredictable and/or too large to list,
		so, a blind try: except: is done. If an exception occurs,
		a UFOLibError will be raised.
		"""
		try:
			f = BytesIO()
			writePlist(obj, f)
			data = f.getvalue()
		except:
			raise UFOLibError("The data for the file %s could not be written because it is not properly formatted." % path)
		self.writeBytesToPath(path, data)

class _NOFS(object):

	def __init__(self, path):
		self._path = path

	def _absPath(self, path):
		return os.path.join(self._path, path)

	def joinPath(self, *parts):
		return os.path.join(*parts)

	def splitPath(self, path):
		return os.path.split(path)

	def directoryName(self, path):
		return os.path.split(path)[0]

	def relativePath(self, path, start):
		return os.path.relpath(path, start)

	def close(self):
		pass

	def open(self, path, mode, encoding=None):
		path = self._absPath(path)
		return open(path, mode=mode, encoding=encoding)

	def remove(self, path):
		path = self._absPath(path)
		os.remove(path)

	def makedir(self, path):
		path = self._absPath(path)
		os.mkdir(path)

	def removedir(self, path):
		path = self._absPath(path)
		shutil.rmtree(path)

	def move(self, path1, path2):
		path1 = self._absPath(path1)
		path2 = self._absPath(path2)
		os.move(path1, path2)

	def movedir(self, path1, path2):
		path1 = self._absPath(path1)
		path2 = self._absPath(path2)
		shutil.move(path1, path2)

	def exists(self, path):
		path = self._absPath(path)
		return os.path.exists(path)

	def isdir(self, path):
		path = self._absPath(path)
		return os.path.isdir(path)

	def listdir(self, path):
		path = self._absPath(path)
		return os.listdir(path)

	def getinfo(self, path):
		path = self._absPath(path)
		stat = os.stat(path)
		info = dict(
			modified_time=stat.st_mtime
		)
		return info

if not haveFS:
	OSFS = _NOFS
<|endoftext|>