<commit_before>"""
Integration with native distribution package managers.
@since: 0.28
"""

# Copyright (C) 2009, <NAME>
# See the README file for details, or visit http://0install.net.

from zeroinstall import _
import os, platform, re, subprocess, sys
from logging import warn, info
from zeroinstall.injector import namespaces, model, arch
from zeroinstall.support import basedir

_dotted_ints = '[0-9]+(?:\.[0-9]+)*'

# This matches a version number that would be a valid Zero Install version without modification
_zeroinstall_regexp = '(?:%s)(?:-(?:pre|rc|post|)(?:%s))*' % (_dotted_ints, _dotted_ints)

# This matches the interesting bits of distribution version numbers
_version_regexp = '(%s)(-r%s)?' % (_zeroinstall_regexp, _dotted_ints)

# We try to do updates atomically without locking, but we don't worry too much about
# duplicate entries or being a little out of sync with the on-disk copy.
class Cache(object):
	def __init__(self, cache_leaf, source, format):
		"""Maintain a cache file (e.g. ~/.cache/0install.net/injector/$name).
		If the size or mtime of $source has changed, or the cache
		format version if different, reset the cache first."""
		self.cache_leaf = cache_leaf
		self.source = source
		self.format = format
		self.cache_dir = basedir.save_cache_path(namespaces.config_site,
							 namespaces.config_prog)
		self.cached_for = {}		# Attributes of source when cache was created
		try:
			self._load_cache()
		except Exception as ex:
			info(_("Failed to load cache (%s). Flushing..."), ex)
			self.flush()

	def flush(self):
		# Wipe the cache
		try:
			info = os.stat(self.source)
			mtime = int(info.st_mtime)
			size = info.st_size
		except Exception as ex:
			warn("Failed to stat %s: %s", self.source, ex)
			mtime = size = 0
		self.cache = {}
		import tempfile
		tmp, tmp_name = tempfile.mkstemp(dir = self.cache_dir)
		data = "mtime=%d\nsize=%d\nformat=%d\n\n" % (mtime, size, self.format)
		while data:
			wrote = os.write(tmp, data)
			data = data[wrote:]
		os.rename(tmp_name, os.path.join(self.cache_dir, self.cache_leaf))

		self._load_cache()

	# Populate self.cache from our saved cache file.
	# Throws an exception if the cache doesn't exist or has the wrong format.
	def _load_cache(self):
		self.cache = cache = {}
		stream = file(os.path.join(self.cache_dir, self.cache_leaf))
		try:
			for line in stream:
				line = line.strip()
				if not line:
					break
				key, value = line.split('=', 1)
				if key in ('mtime', 'size', 'format'):
					self.cached_for[key] = int(value)

			self._check_valid()

			for line in stream:
				key, value = line.split('=', 1)
				cache[key] = value[:-1]
		finally:
			stream.close()

	# Check the source file hasn't changed since we created the cache
	def _check_valid(self):
		info = os.stat(self.source)
		if self.cached_for['mtime'] != int(info.st_mtime):
			raise Exception("Modification time of %s has changed" % self.source)
		if self.cached_for['size'] != info.st_size:
			raise Exception("Size of %s has changed" % self.source)
		if self.cached_for.get('format', None) != self.format:
			raise Exception("Format of cache has changed")

	def get(self, key):
		try:
			self._check_valid()
		except Exception as ex:
			info(_("Cache needs to be refreshed: %s"), ex)
			self.flush()
			return None
		else:
			return self.cache.get(key, None)

	def put(self, key, value):
		cache_path = os.path.join(self.cache_dir, self.cache_leaf)
		self.cache[key] = value
		try:
			stream = file(cache_path, 'a')
			try:
				stream.write('%s=%s\n' % (key, value))
			finally:
				stream.close()
		except Exception as ex:
			warn("Failed to write to cache %s: %s=%s: %s", cache_path, key, value, ex)

def try_cleanup_distro_version(version):
	"""Try to turn a distribution version string into one readable by Zero Install.
	We do this by stripping off anything we can't parse.
	@return: the part we understood, or None if we couldn't parse anything
	@rtype: str"""
	if ':' in version:
		version = version.split(':')[1]	# Skip 'epoch'
	version = version.replace('_', '-')
	match = re.match(_version_regexp, version)
	if match:
		version, revision = match.groups()<commit_msg>Special-case native Java version parsing<commit_after>"""
Integration with native distribution package managers.
@since: 0.28
"""

# Copyright (C) 2009, <NAME>
# See the README file for details, or visit http://0install.net.

from zeroinstall import _
import os, platform, re, subprocess, sys
from logging import warn, info
from zeroinstall.injector import namespaces, model, arch
from zeroinstall.support import basedir

_dotted_ints = '[0-9]+(?:\.[0-9]+)*'

# This matches a version number that would be a valid Zero Install version without modification
_zeroinstall_regexp = '(?:%s)(?:-(?:pre|rc|post|)(?:%s))*' % (_dotted_ints, _dotted_ints)

# This matches the interesting bits of distribution version numbers
# (first bit is for Java-style 6b17 syntax)
_version_regexp = '({ints}b)?({zero})(-r{ints})?'.format(zero = _zeroinstall_regexp, ints = _dotted_ints)

# We try to do updates atomically without locking, but we don't worry too much about
# duplicate entries or being a little out of sync with the on-disk copy.
class Cache(object):
	def __init__(self, cache_leaf, source, format):
		"""Maintain a cache file (e.g. ~/.cache/0install.net/injector/$name).
		If the size or mtime of $source has changed, or the cache
		format version if different, reset the cache first."""
		self.cache_leaf = cache_leaf
		self.source = source
		self.format = format
		self.cache_dir = basedir.save_cache_path(namespaces.config_site,
							 namespaces.config_prog)
		self.cached_for = {}		# Attributes of source when cache was created
		try:
			self._load_cache()
		except Exception as ex:
			info(_("Failed to load cache (%s). Flushing..."), ex)
			self.flush()

	def flush(self):
		# Wipe the cache
		try:
			info = os.stat(self.source)
			mtime = int(info.st_mtime)
			size = info.st_size
		except Exception as ex:
			warn("Failed to stat %s: %s", self.source, ex)
			mtime = size = 0
		self.cache = {}
		import tempfile
		tmp, tmp_name = tempfile.mkstemp(dir = self.cache_dir)
		data = "mtime=%d\nsize=%d\nformat=%d\n\n" % (mtime, size, self.format)
		while data:
			wrote = os.write(tmp, data)
			data = data[wrote:]
		os.rename(tmp_name, os.path.join(self.cache_dir, self.cache_leaf))

		self._load_cache()

	# Populate self.cache from our saved cache file.
	# Throws an exception if the cache doesn't exist or has the wrong format.
	def _load_cache(self):
		self.cache = cache = {}
		stream = file(os.path.join(self.cache_dir, self.cache_leaf))
		try:
			for line in stream:
				line = line.strip()
				if not line:
					break
				key, value = line.split('=', 1)
				if key in ('mtime', 'size', 'format'):
					self.cached_for[key] = int(value)

			self._check_valid()

			for line in stream:
				key, value = line.split('=', 1)
				cache[key] = value[:-1]
		finally:
			stream.close()

	# Check the source file hasn't changed since we created the cache
	def _check_valid(self):
		info = os.stat(self.source)
		if self.cached_for['mtime'] != int(info.st_mtime):
			raise Exception("Modification time of %s has changed" % self.source)
		if self.cached_for['size'] != info.st_size:
			raise Exception("Size of %s has changed" % self.source)
		if self.cached_for.get('format', None) != self.format:
			raise Exception("Format of cache has changed")

	def get(self, key):
		try:
			self._check_valid()
		except Exception as ex:
			info(_("Cache needs to be refreshed: %s"), ex)
			self.flush()
			return None
		else:
			return self.cache.get(key, None)

	def put(self, key, value):
		cache_path = os.path.join(self.cache_dir, self.cache_leaf)
		self.cache[key] = value
		try:
			stream = file(cache_path, 'a')
			try:
				stream.write('%s=%s\n' % (key, value))
			finally:
				stream.close()
		except Exception as ex:
			warn("Failed to write to cache %s: %s=%s: %s", cache_path, key, value, ex)

def try_cleanup_distro_version(version):
	"""Try to turn a distribution version string into one readable by Zero Install.
	We do this by stripping off anything we can't parse.
	@return: the part we understood, or None if we couldn't parse anything
	@rtype: str"""
	if ':' in version:
		version = version.split(':')[1]	# Skip 'epoch'
	version = version.replace('_', '-')
	match = re.match(_version_regexp, version)
	if match:
		major, version, revision = match.groups()
		if major is not None:
			version = major[:-1] + '.' + version
{'fraction_non_alphanumeric': 0.1309889072456927, 'fraction_numerical': 0.004720320981826764, 'mean_word_length': 3.898843930635838, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1760023', 'n_tokens_mistral': 3142, 'n_tokens_neox': 2807, 'n_words': 908}