예제 #1
0
	def from_conf(cls, path=None, **overrides):
		'''Initialize instance from YAML configuration file,
			writing updates (only to keys, specified by "conf_update_keys") back to it.'''
		from onedrive import portalocker
		import yaml

		if path is None:
			path = cls.conf_path_default
			log.debug('Using default state-file path: %r', path)
		path = os.path.expanduser(path)
		with open(path, 'rb') as src:
			portalocker.lock(src, portalocker.LOCK_SH)
			conf = yaml.load(src.read())
			portalocker.unlock(src)
		conf.setdefault('conf_save', path)

		conf_cls = dict()
		for ns, keys in cls.conf_update_keys.viewitems():
			for k in keys:
				try:
					v = conf.get(ns, dict()).get(k)
				except AttributeError:
					if not cls.conf_raise_structure_errors: raise
					raise KeyError((
						'Unable to get value for configuration parameter'
							' "{k}" in section "{ns}", check configuration file (path: {path}) syntax'
							' near the aforementioned section/value.' ).format(ns=ns, k=k, path=path))
				if v is not None: conf_cls['{}_{}'.format(ns, k)] = conf[ns][k]
		conf_cls.update(overrides)

		self = cls(**conf_cls)
		self.conf_save = conf['conf_save']
		return self
예제 #2
0
    def from_conf(cls, path=None, **overrides):
        '''Initialize instance from YAML configuration file,
            writing updates (only to keys, specified by "conf_update_keys") back to it.'''
        from onedrive import portalocker
        import yaml

        if path is None:
            path = cls.conf_path_default
            log.debug('Using default state-file path: %r', path)
        path = os.path.expanduser(path)
        with open(path, 'rb') as src:
            portalocker.lock(src, portalocker.LOCK_SH)
            conf = yaml.load(src.read())
            portalocker.unlock(src)
        conf.setdefault('conf_save', path)

        conf_cls = dict()
        for ns, keys in cls.conf_update_keys.viewitems():
            for k in keys:
                try:
                    v = conf.get(ns, dict()).get(k)
                except AttributeError:
                    if not cls.conf_raise_structure_errors: raise
                    raise KeyError(
                        'Unable to get value for configuration parameter'
                        ' "{k}" in section "{ns}", check configuration file (path: {path}) syntax'
                        ' near the aforementioned section/value.'.format(
                            ns=ns, k=k, path=path))
                if v is not None:
                    conf_cls['{}_{}'.format(ns, k)] = conf[ns][k]
        conf_cls.update(overrides)

        self = cls(**conf_cls)
        self.conf_save = conf['conf_save']
        return self
예제 #3
0
    def from_conf(cls, path=None, **overrides):
        '''Initialize instance from YAML configuration file,
			writing updates (only to keys, specified by "conf_update_keys") back to it.'''
        from onedrive import portalocker
        import yaml

        if path is None:
            path = cls.conf_path_default
            log.debug('Using default state-file path: %r', path)
        path = os.path.expanduser(path)
        with open(path, 'rb') as src:
            portalocker.lock(src, portalocker.LOCK_SH)
            yaml_str = src.read()
            portalocker.unlock(src)
        conf = yaml.safe_load(yaml_str)
        conf.setdefault('conf_save', path)

        conf_cls = dict()
        for ns, keys in cls.conf_update_keys.viewitems():
            for k in keys:
                try:
                    v = conf.get(ns, dict()).get(k)
                except AttributeError:
                    if not cls.conf_raise_structure_errors: raise
                    raise KeyError((
                        'Unable to get value for configuration parameter'
                        ' "{k}" in section "{ns}", check configuration file (path: {path}) syntax'
                        ' near the aforementioned section/value.').format(
                            ns=ns, k=k, path=path))
                if v is not None: conf_cls['{}_{}'.format(ns, k)] = conf[ns][k]
        conf_cls.update(overrides)

        # Hack to work around YAML parsing client_id of e.g. 000123 as an octal int
        if isinstance(conf.get('client', dict()).get('id'), (int, long)):
            log.warn(
                'Detected client_id being parsed as an integer (as per yaml), trying to un-mangle it.'
                ' If requests will still fail afterwards, please replace it in the configuration file (path: %r),'
                ' also putting single or double quotes (either one should work) around the value.',
                path)
            cid = conf['client']['id']
            if not re.search(r'\b(0*)?{:d}\b'.format(cid), yaml_str)\
              and re.search(r'\b(0*)?{:o}\b'.format(cid), yaml_str):
                cid = int('{:0}'.format(cid))
            conf['client']['id'] = '{:016d}'.format(cid)

        self = cls(**conf_cls)
        self.conf_save = conf['conf_save']
        return self
예제 #4
0
	def from_conf(cls, path=None, **overrides):
		'''Initialize instance from YAML configuration file,
			writing updates (only to keys, specified by "conf_update_keys") back to it.'''
		from onedrive import portalocker
		import yaml

		if path is None:
			path = cls.conf_path_default
			log.debug('Using default state-file path: %r', path)
		path = os.path.expanduser(path)
		with open(path, 'rb') as src:
			portalocker.lock(src, portalocker.LOCK_SH)
			yaml_str = src.read()
			portalocker.unlock(src)
		conf = yaml.safe_load(yaml_str)
		conf.setdefault('conf_save', path)

		conf_cls = dict()
		for ns, keys in cls.conf_update_keys.viewitems():
			for k in keys:
				try:
					v = conf.get(ns, dict()).get(k)
				except AttributeError:
					if not cls.conf_raise_structure_errors: raise
					raise KeyError((
						'Unable to get value for configuration parameter'
							' "{k}" in section "{ns}", check configuration file (path: {path}) syntax'
							' near the aforementioned section/value.' ).format(ns=ns, k=k, path=path))
				if v is not None: conf_cls['{}_{}'.format(ns, k)] = conf[ns][k]
		conf_cls.update(overrides)

		# Hack to work around YAML parsing client_id of e.g. 000123 as an octal int
		if isinstance(conf.get('client', dict()).get('id'), (int, long)):
			log.warn( 'Detected client_id being parsed as an integer (as per yaml), trying to un-mangle it.'
				' If requests will still fail afterwards, please replace it in the configuration file (path: %r),'
					' also putting single or double quotes (either one should work) around the value.', path )
			cid = conf['client']['id']
			if not re.search(r'\b(0*)?{:d}\b'.format(cid), yaml_str)\
					and re.search(r'\b(0*)?{:o}\b'.format(cid), yaml_str):
				cid = int('{:0}'.format(cid))
			conf['client']['id'] = '{:016d}'.format(cid)

		self = cls(**conf_cls)
		self.conf_save = conf['conf_save']
		return self
예제 #5
0
    def sync(self):
        if not self.conf_save:
            return
        from onedrive import portalocker
        import yaml

        retry = False
        with open(self.conf_save, "r+b") as src:
            portalocker.lock(src, portalocker.LOCK_SH)
            conf_raw = src.read()
            conf = yaml.load(io.BytesIO(conf_raw)) if conf_raw else dict()
            portalocker.unlock(src)

            conf_updated = False
            for ns, keys in self.conf_update_keys.viewitems():
                for k in keys:
                    v = getattr(self, "{}_{}".format(ns, k), None)
                    if isinstance(v, unicode):
                        v = v.encode("utf-8")
                    if v != conf.get(ns, dict()).get(k):
                        # log.debug(
                        # 	'Different val ({}.{}): {!r} != {!r}'\
                        # 	.format(ns, k, v, conf.get(ns, dict()).get(k)) )
                        conf.setdefault(ns, dict())[k] = v
                        conf_updated = True

            if conf_updated:
                log.debug("Updating configuration file (%r)", src.name)
                conf_new = yaml.safe_dump(conf, default_flow_style=False)
                if os.name == "nt":
                    # lockf + tempfile + rename doesn't work on windows due to
                    #  "[Error 32] ... being used by another process",
                    #  so this update can potentially leave broken file there
                    # Should probably be fixed by someone who uses/knows about windows
                    portalocker.lock(src, portalocker.LOCK_EX)
                    src.seek(0)
                    if src.read() != conf_raw:
                        retry = True
                    else:
                        src.seek(0)
                        src.truncate()
                        src.write(conf_new)
                        src.flush()
                        portalocker.unlock(src)

                else:
                    with tempfile.NamedTemporaryFile(
                        prefix="{}.".format(basename(self.conf_save)), dir=dirname(self.conf_save), delete=False
                    ) as tmp:
                        try:
                            portalocker.lock(src, portalocker.LOCK_EX)
                            src.seek(0)
                            if src.read() != conf_raw:
                                retry = True
                            else:
                                portalocker.lock(tmp, portalocker.LOCK_EX)
                                tmp.write(conf_new)
                                os.fchmod(tmp.fileno(), stat.S_IMODE(os.fstat(src.fileno()).st_mode))
                                os.rename(tmp.name, src.name)
                                # Non-atomic update for pids that already have fd to old file,
                                #  but (presumably) are waiting for the write-lock to be released
                                src.seek(0)
                                src.truncate()
                                src.write(conf_new)
                        finally:
                            try:
                                os.unlink(tmp.name)
                            except OSError:
                                pass

        if retry:
            log.debug("Configuration file (%r) was changed" " during merge, restarting merge", self.conf_save)
            return self.sync()
예제 #6
0
	def sync(self):
		if not self.conf_save: return
		from onedrive import portalocker
		import yaml

		retry = False
		with open(self.conf_save, 'r+b') as src:
			portalocker.lock(src, portalocker.LOCK_SH)
			conf_raw = src.read()
			conf = yaml.load(io.BytesIO(conf_raw)) if conf_raw else dict()
			portalocker.unlock(src)

			conf_updated = False
			for ns, keys in self.conf_update_keys.viewitems():
				for k in keys:
					v = getattr(self, '{}_{}'.format(ns, k), None)
					if isinstance(v, unicode): v = v.encode('utf-8')
					if v != conf.get(ns, dict()).get(k):
						# log.debug(
						# 	'Different val ({}.{}): {!r} != {!r}'\
						# 	.format(ns, k, v, conf.get(ns, dict()).get(k)) )
						conf.setdefault(ns, dict())[k] = v
						conf_updated = True

			if conf_updated:
				log.debug('Updating configuration file (%r)', src.name)
				conf_new = yaml.safe_dump(conf, default_flow_style=False)
				if os.name == 'nt':
					# lockf + tempfile + rename doesn't work on windows due to
					#  "[Error 32] ... being used by another process",
					#  so this update can potentially leave broken file there
					# Should probably be fixed by someone who uses/knows about windows
					portalocker.lock(src, portalocker.LOCK_EX)
					src.seek(0)
					if src.read() != conf_raw: retry = True
					else:
						src.seek(0)
						src.truncate()
						src.write(conf_new)
						src.flush()
						portalocker.unlock(src)

				else:
					with tempfile.NamedTemporaryFile(
							prefix='{}.'.format(basename(self.conf_save)),
							dir=dirname(self.conf_save), delete=False) as tmp:
						try:
							portalocker.lock(src, portalocker.LOCK_EX)
							src.seek(0)
							if src.read() != conf_raw: retry = True
							else:
								portalocker.lock(tmp, portalocker.LOCK_EX)
								tmp.write(conf_new)
								os.fchmod(tmp.fileno(), stat.S_IMODE(os.fstat(src.fileno()).st_mode))
								os.rename(tmp.name, src.name)
								# Non-atomic update for pids that already have fd to old file,
								#  but (presumably) are waiting for the write-lock to be released
								src.seek(0)
								src.truncate()
								src.write(conf_new)
						finally:
							try: os.unlink(tmp.name)
							except OSError: pass

		if retry:
			log.debug(
				'Configuration file (%r) was changed'
					' during merge, restarting merge', self.conf_save )
			return self.sync()