<commit_before># -*- coding: utf-8 -*-
#
# ur_cb2 documentation build configuration file, created by
# sphinx-quickstart on Mon Mar 21 18:24:05 2016.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys
import os
import sphinx_rtd_theme

os.system("sphinx-apidoc -f -o ./_modules ../ur_cb2")

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('../'))

def skip(app, what, name, obj, skip, options):
    if name == "__init__":
        return False
    return skip

def setup(app):
    app.connect("autodoc-skip-member", skip)

# -- General configuration ------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.todo',
    'sphinx.ext.coverage',
    'sphinx.ext.mathjax',
    'sphinx.ext.viewcode',
    'sphinx.ext.napoleon'
]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'

# The encoding of source files.
#source_encoding = 'utf-8-sig'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = u'ur_cb2'
copyright = u'2016, GTRC'
author = u'Michael Sobrepera'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = u'0.2'
# The full version, including alpha/beta/rc tags.
release = u'0.2'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build','setup.py','*setup.py']

# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
show_authors = True

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True

# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
if on_rtd:
    html_theme = '/default.css'
else:
    html_theme = 'default'

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}

# Add any paths that contain custom themes here, relative to this directory.
# html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
#html_title = None

# A shorter title for the navigation bar.  Default is the same as html_title.
#html_short_title = None

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
html_logo = 'logo.png'

# The name of an image file (relative to this directory) to use as a favicon of
# the docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If false, no module index is generated.
#html_domain_indices = True

# If false, no index is generated.
#html_use_index = True

# If true, the index is split into individual pages for each letter.
#html_split_index = False

# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True

# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True

# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True

# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it.  The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''

# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None

# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
#   'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
#   'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
#html_search_language = 'en'

# A dictionary with options for the search language support, empty by default.
# Now only 'ja' uses this config value
#html_search_options = {'type': 'default'}

# The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used.
#html_search_scorer = 'scorer.js'

# Output file base name for HTML help builder.
htmlhelp_basename = 'ur_cb2doc'

# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',

# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',

# Additional stuff for the LaTeX preamble.
#'preamble': '',

# Latex figure (float) alignment
#'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
    (master_doc, 'ur_cb2.tex', u'ur\\_cb2 Documentation',
     u'Michael Sobrepera', 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False

# If true, show page references after internal links.
#latex_show_pagerefs = False

# If true, show URL addresses after external links.
#latex_show_urls = False

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_domain_indices = True

# -- Options for manual page output ---------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    (master_doc, 'ur_cb2', u'ur_cb2 Documentation',
     [author], 1)
]

# If true, show URL addresses after external links.
#man_show_urls = False

# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
    (master_doc, 'ur_cb2', u'ur_cb2 Documentation',
     author, 'ur_cb2', 'One line description of project.',
     'Miscellaneous'),
]

# Documents to append as an appendix to all manuals.
#texinfo_appendices = []

# If false, no module index is generated.
#texinfo_domain_indices = True

# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'

# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
#
<commit_msg>corrected wrong flag for default<commit_after># -*- coding: utf-8 -*-
#
# ur_cb2 documentation build configuration file, created by
# sphinx-quickstart on Mon Mar 21 18:24:05 2016.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys
import os
import sphinx_rtd_theme

os.system("sphinx-apidoc -f -o ./_modules ../ur_cb2")

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('../'))

def skip(app, what, name, obj, skip, options):
    if name == "__init__":
        return False
    return skip

def setup(app):
    app.connect("autodoc-skip-member", skip)

# -- General configuration ------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.todo',
    'sphinx.ext.coverage',
    'sphinx.ext.mathjax',
    'sphinx.ext.viewcode',
    'sphinx.ext.napoleon'
]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'

# The encoding of source files.
#source_encoding = 'utf-8-sig'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = u'ur_cb2'
copyright = u'2016, GTRC'
author = u'Michael Sobrepera'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = u'0.2'
# The full version, including alpha/beta/rc tags.
release = u'0.2'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build','setup.py','*setup.py']

# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
show_authors = True

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True

# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
# on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
# if on_rtd:
#     html_theme = '/default.css'
# else:
html_theme = 'default'
html_style = '/default.css'

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}

# Add any paths that contain custom themes here, relative to this directory.
# html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
#html_title = None

# A shorter title for the navigation bar.  Default is the same as html_title.
#html_short_title = None

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
html_logo = 'logo.png'

# The name of an image file (relative to this directory) to use as a favicon of
# the docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If false, no module index is generated.
#html_domain_indices = True

# If false, no index is generated.
#html_use_index = True

# If true, the index is split into individual pages for each letter.
#html_split_index = False

# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True

# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True

# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True

# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it.  The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''

# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None

# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
#   'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
#   'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
#html_search_language = 'en'

# A dictionary with options for the search language support, empty by default.
# Now only 'ja' uses this config value
#html_search_options = {'type': 'default'}

# The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used.
#html_search_scorer = 'scorer.js'

# Output file base name for HTML help builder.
htmlhelp_basename = 'ur_cb2doc'

# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',

# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',

# Additional stuff for the LaTeX preamble.
#'preamble': '',

# Latex figure (float) alignment
#'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
    (master_doc, 'ur_cb2.tex', u'ur\\_cb2 Documentation',
     u'Michael Sobrepera', 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False

# If true, show page references after internal links.
#latex_show_pagerefs = False

# If true, show URL addresses after external links.
#latex_show_urls = False

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_domain_indices = True

# -- Options for manual page output ---------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    (master_doc, 'ur_cb2', u'ur_cb2 Documentation',
     [author], 1)
]

# If true, show URL addresses after external links.
#man_show_urls = False

# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
    (master_doc, 'ur_cb2', u'ur_cb2 Documentation',
     author, 'ur_cb2', 'One line description of project.',
     'Miscellaneous'),
]

# Documents to append as an appendix to all manuals.
#texinfo_appendices = []

# If false, no module index is generated.
#texinfo_domain_indices = True

# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'

# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
#
<|endoftext|>


<commit_before>import logging
import random
import time
from calendar import timegm
from itertools import chain
from .actions import Attack, BeginTurn, Death, Deaths, EndTurn, EventListener
from .card import Card, THE_COIN
from .entity import Entity
from .enums import CardType, PlayState, Step, Zone
from .managers import GameManager
from .utils import CardList

class GameOver(Exception):
	pass

class BaseGame(Entity):
	type = CardType.GAME
	MAX_MINIONS_ON_FIELD = 8
	Manager = GameManager

	def __init__(self, players):
		self.data = None
		super().__init__()
		self.players = players
		for player in players:
			player.game = self
		self.step = None
		self.next_step = None
		self.turn = 0
		self.current_player = None
		self.auras = []
		self.minions_killed = CardList()
		self.minions_killed_this_turn = CardList()
		self._action_queue = []
		self.game_over = False

	def __repr__(self):
		return "<%s %s>" % (self.__class__.__name__, self)

	def __str__(self):
		return "%s vs %s" % (self.players)

	def __iter__(self):
		return self.all_entities.__iter__()

	@property
	def board(self):
		return CardList(chain(self.players[0].field, self.players[1].field))

	@property
	def decks(self):
		return CardList(chain(self.players[0].deck, self.players[1].deck))

	@property
	def hands(self):
		return CardList(chain(self.players[0].hand, self.players[1].hand))

	@property
	def characters(self):
		return CardList(chain(self.players[0].characters, self.players[1].characters))

	@property
	def all_entities(self):
		return CardList(chain(self.entities, self.hands, self.decks))

	@property
	def entities(self):
		return CardList(chain([self], self.players[0].entities, self.players[1].entities))

	@property
	def live_entities(self):
		return CardList(chain(self.players[0].live_entities, self.players[1].live_entities))

	def filter(self, *args, **kwargs):
		return self.all_entities.filter(*args, **kwargs)

	def attack(self, source, target):
		return self.queue_actions(source, [Attack(source, target)])

	def _attack(self):
		"""
		See https://github.com/jleclanche/fireplace/wiki/Combat
		for information on how attacking works
		"""
		attacker = self.proposed_attacker
		defender = self.proposed_defender
		self.proposed_attacker = None
		self.proposed_defender = None
		if attacker.should_exit_combat:
			logging.info("Attack has been interrupted.")
			attacker.should_exit_combat = False
			attacker.attacking = False
			defender.defending = False
			return
		# Save the attacker/defender atk values in case they change during the attack
		# (eg. in case of Enrage)
		def_atk = defender.atk
		attacker.hit(defender, attacker.atk)
		if def_atk:
			defender.hit(attacker, def_atk)
		attacker.attacking = False
		defender.defending = False
		attacker.num_attacks += 1

	def play(self, card):
		"""
		Plays \a card from a Player's hand
		"""
		player = card.controller
		logging.info("%s plays %r", player, card)
		cost = card.cost
		if player.temp_mana:
			# The coin, Innervate etc
			cost -= player.temp_mana
			player.temp_mana = max(0, player.temp_mana - card.cost)
		player.used_mana += cost
		if card.overload:
			logging.info("%s overloads for %i mana", player, card.overload)
			player.overloaded += card.overload
		player.last_card_played = card
		player.summon(card)
		player.combo = True
		player.cards_played_this_turn += 1
		if card.type == CardType.MINION:
			player.minions_played_this_turn += 1

	def card(self, id):
		card = Card(id)
		self.manager.new_entity(card)
		return card

	def check_for_end_game(self):
		"""
		Check if one or more player is currently losing.
		End the game if they are.
		"""
		gameover = False
		for player in self.players:
			if player.playstate == PlayState.LOSING:
				player.playstate = PlayState.LOST
				gameover = True
			else:
				player.playstate = PlayState.WON

		if gameover:
			#raise GameOver("The game has ended.")
			self.game_over = True

	def process_deaths(self):
		actions = []
		for card in self.live_entities:
			if card.to_be_destroyed:
				actions += self._schedule_death(card)

		self.check_for_end_game()

		if not self.game_over and actions:
			self.queue_actions(self, actions)

	def _schedule_death(self, card):
		"""
		Prepare a card for its death. Will run any related Death
		trigger attached to the Game object.
		Returns a list of actions to perform during the death sweep.
		"""
		card.ignore_events = True
		if card.type == CardType.MINION:
			self.minions_killed.append(card)
			self.minions_killed_this_turn.append(card)
			card.controller.minions_killed_this_turn += 1
		elif card.type == CardType.HERO:
			card.controller.playstate = PlayState.LOSING

		return [Death(card)]

	def queue_actions(self, source, actions):
		"""
		Queue a list of \a actions for processing from \a source.
		"""
		ret = []
		for action in actions:
			if isinstance(action, EventListener):
				logging.debug("Registering %r on %r", action, self)
				source.controller._events.append(action)
			else:
				self._action_queue.append(action)
				ret.append(action.trigger(source, self))
				self.refresh_auras()
				self._action_queue.pop()
		if not self._action_queue:
			self.process_deaths()

		return ret

	def pick_first_player(self):
		"""
		Picks and returns first player, second player
		In the default implementation, the first player is always
		"Player 0". Use CoinRules to decide it randomly.
		"""
		return self.players[0], self.players[1]

	def refresh_auras(self):
		for aura in self.auras:
			aura.update()

	def prepare(self):
		self.players[0].opponent = self.players[1]
		self.players[1].opponent = self.players[0]
		for player in self.players:
			self.manager.new_entity(player)
			player.zone = Zone.PLAY
			player.summon(player.original_deck.hero)
			for card in player.original_deck:
				card.controller = player
				card.zone = Zone.DECK
			player.shuffle_deck()
			player.playstate = PlayState.PLAYING
			player.cards_drawn_this_turn = 0

		first, second = self.pick_first_player()
		self.player1 = first
		self.player1.first_player = True
		self.player2 = second
		self.player2.first_player = False
		self.player1.draw(3)
		self.player2.draw(4)
		self.current_player = self.player1

	def start(self):
		logging.info("Starting game: %r" % (self))
		self.prepare()
		self.begin_turn(self.player1)

	def end_turn(self):
		return self.queue_actions(self, [EndTurn(self.current_player)])

	def _end_turn(self):
		logging.info("%s ends turn %i", self.current_player, self.turn)
		self.step, self.next_step = self.next_step, Step.MAIN_CLEANUP

		self.current_player.temp_mana = 0
		for character in self.current_player.characters.filter(frozen=True):
			if not character.num_attacks:
				character.frozen = False
		for buff in self.current_player.entities.filter(one_turn_effect=True):
			logging.info("Ending One-Turn effect: %r", buff)
			buff.destroy()

		self.step, self.next_step = self.next_step, Step.MAIN_NEXT
		self.begin_turn(self.current_player.opponent)

	def begin_turn(self, player):
		return self.queue_actions(self, [BeginTurn(player)])

	def _begin_turn(self, player):
		self.step, self.next_step = self.next_step, Step.MAIN_START_TRIGGERS
		self.step, self.next_step = self.next_step, Step.MAIN_START
		self.turn += 1
		logging.info("%s begins turn %i", player, self.turn)
		self.step, self.next_step = self.next_step, Step.MAIN_ACTION
		self.current_player = player
		self.minions_killed_this_turn = CardList()

		for p in self.players:
			p.cards_drawn_this_turn = 0
			p.current_player = p is player

		player.turn_start = timegm(time.gmtime())
		player.cards_played_this_turn = 0
		player.minions_played_this_turn = 0
		player.minions_killed_this_turn = 0
		player.combo = False
		player.max_mana += 1
		player.used_mana = player.overloaded
		player.overloaded = 0
		for entity in player.entities:
			if entity.type != CardType.PLAYER:
				entity.turns_in_play += 1
				if entity.type == CardType.HERO_POWER:
					entity.exhausted = False
				elif entity.type in (CardType.HERO, CardType.MINION):
					entity.num_attacks = 0

		player.draw()

class CoinRules:
	"""
	Randomly determines the starting player when the Game starts.
	The second player gets "The Coin" (GAME_005).
	"""
	def pick_first_player(self):
		winner = random.choice(self.players)
		logging.info("Tossing the coin... %s wins!", winner)
		return winner, winner.opponent

	def start(self):
		super().start()
		logging.info("%s gets The Coin (%s)", self.player2, THE_COIN)
		self.player2.give(THE_COIN)

class MulliganRules:
	"""
	Performs a Mulligan phase when the Game starts.
	Currently just a dummy phase.
	"""
	def start(self):
		self.next_step = Step.BEGIN_MULLIGAN
		super().start()
		self.begin_mulligan()

	def begin_mulligan(self):
		logging.info("Entering mulligan phase")
		self.step, self.next_step = self.next_step, Step.MAIN_READY

class Game(MulliganRules, CoinRules, BaseGame):
	pass
<commit_msg>Removed a game_over check<commit_after>import logging
import random
import time
from calendar import timegm
from itertools import chain
from .actions import Attack, BeginTurn, Death, Deaths, EndTurn, EventListener
from .card import Card, THE_COIN
from .entity import Entity
from .enums import CardType, PlayState, Step, Zone
from .managers import GameManager
from .utils import CardList

class GameOver(Exception):
	pass

class BaseGame(Entity):
	type = CardType.GAME
	MAX_MINIONS_ON_FIELD = 8
	Manager = GameManager

	def __init__(self, players):
		self.data = None
		super().__init__()
		self.players = players
		for player in players:
			player.game = self
		self.step = None
		self.next_step = None
		self.turn = 0
		self.current_player = None
		self.auras = []
		self.minions_killed = CardList()
		self.minions_killed_this_turn = CardList()
		self._action_queue = []

	def __repr__(self):
		return "<%s %s>" % (self.__class__.__name__, self)

	def __str__(self):
		return "%s vs %s" % (self.players)

	def __iter__(self):
		return self.all_entities.__iter__()

	@property
	def board(self):
		return CardList(chain(self.players[0].field, self.players[1].field))

	@property
	def decks(self):
		return CardList(chain(self.players[0].deck, self.players[1].deck))

	@property
	def hands(self):
		return CardList(chain(self.players[0].hand, self.players[1].hand))

	@property
	def characters(self):
		return CardList(chain(self.players[0].characters, self.players[1].characters))

	@property
	def all_entities(self):
		return CardList(chain(self.entities, self.hands, self.decks))

	@property
	def entities(self):
		return CardList(chain([self], self.players[0].entities, self.players[1].entities))

	@property
	def live_entities(self):
		return CardList(chain(self.players[0].live_entities, self.players[1].live_entities))

	def filter(self, *args, **kwargs):
		return self.all_entities.filter(*args, **kwargs)

	def attack(self, source, target):
		return self.queue_actions(source, [Attack(source, target)])

	def _attack(self):
		"""
		See https://github.com/jleclanche/fireplace/wiki/Combat
		for information on how attacking works
		"""
		attacker = self.proposed_attacker
		defender = self.proposed_defender
		self.proposed_attacker = None
		self.proposed_defender = None
		if attacker.should_exit_combat:
			logging.info("Attack has been interrupted.")
			attacker.should_exit_combat = False
			attacker.attacking = False
			defender.defending = False
			return
		# Save the attacker/defender atk values in case they change during the attack
		# (eg. in case of Enrage)
		def_atk = defender.atk
		attacker.hit(defender, attacker.atk)
		if def_atk:
			defender.hit(attacker, def_atk)
		attacker.attacking = False
		defender.defending = False
		attacker.num_attacks += 1

	def play(self, card):
		"""
		Plays \a card from a Player's hand
		"""
		player = card.controller
		logging.info("%s plays %r", player, card)
		cost = card.cost
		if player.temp_mana:
			# The coin, Innervate etc
			cost -= player.temp_mana
			player.temp_mana = max(0, player.temp_mana - card.cost)
		player.used_mana += cost
		if card.overload:
			logging.info("%s overloads for %i mana", player, card.overload)
			player.overloaded += card.overload
		player.last_card_played = card
		player.summon(card)
		player.combo = True
		player.cards_played_this_turn += 1
		if card.type == CardType.MINION:
			player.minions_played_this_turn += 1

	def card(self, id):
		card = Card(id)
		self.manager.new_entity(card)
		return card

	def check_for_end_game(self):
		"""
		Check if one or more player is currently losing.
		End the game if they are.
		"""
		gameover = False
		for player in self.players:
			if player.playstate == PlayState.LOSING:
				player.playstate = PlayState.LOST
				gameover = True
			else:
				player.playstate = PlayState.WON

		if gameover:
			raise GameOver("The game has ended.")

	def process_deaths(self):
		actions = []
		for card in self.live_entities:
			if card.to_be_destroyed:
				actions += self._schedule_death(card)

		self.check_for_end_game()

		if actions:
			self.queue_actions(self, actions)

	def _schedule_death(self, card):
		"""
		Prepare a card for its death. Will run any related Death
		trigger attached to the Game object.
		Returns a list of actions to perform during the death sweep.
		"""
		card.ignore_events = True
		if card.type == CardType.MINION:
			self.minions_killed.append(card)
			self.minions_killed_this_turn.append(card)
			card.controller.minions_killed_this_turn += 1
		elif card.type == CardType.HERO:
			card.controller.playstate = PlayState.LOSING

		return [Death(card)]

	def queue_actions(self, source, actions):
		"""
		Queue a list of \a actions for processing from \a source.
		"""
		ret = []
		for action in actions:
			if isinstance(action, EventListener):
				logging.debug("Registering %r on %r", action, self)
				source.controller._events.append(action)
			else:
				self._action_queue.append(action)
				ret.append(action.trigger(source, self))
				self.refresh_auras()
				self._action_queue.pop()
		if not self._action_queue:
			self.process_deaths()

		return ret

	def pick_first_player(self):
		"""
		Picks and returns first player, second player
		In the default implementation, the first player is always
		"Player 0". Use CoinRules to decide it randomly.
		"""
		return self.players[0], self.players[1]

	def refresh_auras(self):
		for aura in self.auras:
			aura.update()

	def prepare(self):
		self.players[0].opponent = self.players[1]
		self.players[1].opponent = self.players[0]
		for player in self.players:
			self.manager.new_entity(player)
			player.zone = Zone.PLAY
			player.summon(player.original_deck.hero)
			for card in player.original_deck:
				card.controller = player
				card.zone = Zone.DECK
			player.shuffle_deck()
			player.playstate = PlayState.PLAYING
			player.cards_drawn_this_turn = 0

		first, second = self.pick_first_player()
		self.player1 = first
		self.player1.first_player = True
		self.player2 = second
		self.player2.first_player = False
		self.player1.draw(3)
		self.player2.draw(4)
		self.current_player = self.player1

	def start(self):
		logging.info("Starting game: %r" % (self))
		self.prepare()
		self.begin_turn(self.player1)

	def end_turn(self):
		return self.queue_actions(self, [EndTurn(self.current_player)])

	def _end_turn(self):
		logging.info("%s ends turn %i", self.current_player, self.turn)
		self.step, self.next_step = self.next_step, Step.MAIN_CLEANUP

		self.current_player.temp_mana = 0
		for character in self.current_player.characters.filter(frozen=True):
			if not character.num_attacks:
				character.frozen = False
		for buff in self.current_player.entities.filter(one_turn_effect=True):
			logging.info("Ending One-Turn effect: %r", buff)
			buff.destroy()

		self.step, self.next_step = self.next_step, Step.MAIN_NEXT
		self.begin_turn(self.current_player.opponent)

	def begin_turn(self, player):
		return self.queue_actions(self, [BeginTurn(player)])

	def _begin_turn(self, player):
		self.step, self.next_step = self.next_step, Step.MAIN_START_TRIGGERS
		self.step, self.next_step = self.next_step, Step.MAIN_START
		self.turn += 1
		logging.info("%s begins turn %i", player, self.turn)
		self.step, self.next_step = self.next_step, Step.MAIN_ACTION
		self.current_player = player
		self.minions_killed_this_turn = CardList()

		for p in self.players:
			p.cards_drawn_this_turn = 0
			p.current_player = p is player

		player.turn_start = timegm(time.gmtime())
		player.cards_played_this_turn = 0
		player.minions_played_this_turn = 0
		player.minions_killed_this_turn = 0
		player.combo = False
		player.max_mana += 1
		player.used_mana = player.overloaded
		player.overloaded = 0
		for entity in player.entities:
			if entity.type != CardType.PLAYER:
				entity.turns_in_play += 1
				if entity.type == CardType.HERO_POWER:
					entity.exhausted = False
				elif entity.type in (CardType.HERO, CardType.MINION):
					entity.num_attacks = 0

		player.draw()

class CoinRules:
	"""
	Randomly determines the starting player when the Game starts.
	The second player gets "The Coin" (GAME_005).
	"""
	def pick_first_player(self):
		winner = random.choice(self.players)
		logging.info("Tossing the coin... %s wins!", winner)
		return winner, winner.opponent

	def start(self):
		super().start()
		logging.info("%s gets The Coin (%s)", self.player2, THE_COIN)
		self.player2.give(THE_COIN)

class MulliganRules:
	"""
	Performs a Mulligan phase when the Game starts.
	Currently just a dummy phase.
	"""
	def start(self):
		self.next_step = Step.BEGIN_MULLIGAN
		super().start()
		self.begin_mulligan()

	def begin_mulligan(self):
		logging.info("Entering mulligan phase")
		self.step, self.next_step = self.next_step, Step.MAIN_READY

class Game(MulliganRules, CoinRules, BaseGame):
	pass
<|endoftext|>


<commit_before>"""Render controller for rendering documents."""

import mimetypes
import sys
from grow.templates import tags
from grow.templates import doc_dependency
from . import controllers
from . import env
from . import errors
from . import messages
from . import ui

class RenderedController(controllers.BaseController):
    KIND = messages.Kind.RENDERED

    def __init__(self, view=None, doc=None, path=None, _pod=None):
        self.view = view
        self.path = path
        if doc:
            self._pod_path = doc.pod_path
            self._locale = str(doc.locale)
        else:
            self._pod_path = None
            self._locale = None
        super(RenderedController, self).__init__(_pod=_pod)

    def __repr__(self):
        doc = self.doc
        if not doc:
            return '<Rendered(view=\'{}\')>'.format(self.view)
        if doc.locale:
            return '<Rendered(view=\'{}\', doc=\'{}\', locale=\'{}\')>'.format(
                self.view, doc.pod_path, str(doc.locale))
        return '<Rendered(view=\'{}\', doc=\'{}\')>'.format(
            self.view, doc.pod_path)

    def get_mimetype(self, params=None):
        return mimetypes.guess_type(self.view)[0]

    @property
    def doc(self):
        # Rely on the pod caching to get the doc so that the routing map
        # does not need to be rebuilt every time a document changes.
        if self._pod_path is None:
            return None

        return self.pod.get_doc(self._pod_path, self._locale)

    @property
    def locale(self):
        doc = self.doc
        return doc.locale if doc else None

    def list_concrete_paths(self):
        if self.path:
            return [self.path]
        if not self.doc:
            return []
        return [self.doc.get_serving_path()]

    def render(self, params, inject=True):
        doc = self.doc
        preprocessor = None
        translator = None
        if inject:
            preprocessor = self.pod.inject_preprocessors(doc=doc)
            translator = self.pod.inject_translators(doc=doc)
        jinja_env = self.pod.get_jinja_env(self.locale)
        template = jinja_env.get_template(self.view.lstrip('/'))
        track_dependency = doc_dependency.DocDependency(doc)
        local_tags = tags.create_builtin_tags(
            self.pod, doc, track_dependency=track_dependency)

        # Track the message stats, including untranslated strings.
        if self.pod.is_enabled(self.pod.FEATURE_TRANSLATION_STATS):
            template.globals['_'] = tags.make_doc_gettext(doc)

        try:
            kwargs = {
                'doc': doc,
                'env': self.pod.env,
                'podspec': self.pod.podspec,
                'g': local_tags,
                '_track_dependency': track_dependency,
            }
            content = template.render(kwargs).lstrip()
            if self.pod.is_enabled(self.pod.FEATURE_UI):
                content = self._inject_ui(
                    content, preprocessor, translator)
            return content
        except Exception as e:
            text = 'Error building {}: {}'
            exception = errors.BuildError(text.format(self, e))
            exception.traceback = sys.exc_info()[2]
            exception.controller = self
            exception.exception = e
            raise exception

    def _inject_ui(self, content, preprocessor, translator):
        if not self.get_mimetype().endswith('html'):
            return content

        ui_settings = self.pod.ui
        show_ui = (self.pod.env.name == env.Name.DEV
                   and (preprocessor or translator))
        if ui_settings or show_ui:
            jinja_env = ui.create_jinja_env()
            ui_template = jinja_env.get_template('ui.html')
            content += '\n' + ui_template.render({
                'doc': self.doc,
                'preprocessor': preprocessor,
                'translator': translator,
                'ui': ui_settings,
            })

        return content
<commit_msg>Fixing up the globals for `g` since it currently doesn't work for macros.<commit_after>"""Render controller for rendering documents."""

import mimetypes
import sys
from grow.templates import tags
from grow.templates import doc_dependency
from . import controllers
from . import env
from . import errors
from . import messages
from . import ui

class RenderedController(controllers.BaseController):
    KIND = messages.Kind.RENDERED

    def __init__(self, view=None, doc=None, path=None, _pod=None):
        self.view = view
        self.path = path
        if doc:
            self._pod_path = doc.pod_path
            self._locale = str(doc.locale)
        else:
            self._pod_path = None
            self._locale = None
        super(RenderedController, self).__init__(_pod=_pod)

    def __repr__(self):
        doc = self.doc
        if not doc:
            return '<Rendered(view=\'{}\')>'.format(self.view)
        if doc.locale:
            return '<Rendered(view=\'{}\', doc=\'{}\', locale=\'{}\')>'.format(
                self.view, doc.pod_path, str(doc.locale))
        return '<Rendered(view=\'{}\', doc=\'{}\')>'.format(
            self.view, doc.pod_path)

    def get_mimetype(self, params=None):
        return mimetypes.guess_type(self.view)[0]

    @property
    def doc(self):
        # Rely on the pod caching to get the doc so that the routing map
        # does not need to be rebuilt every time a document changes.
        if self._pod_path is None:
            return None

        return self.pod.get_doc(self._pod_path, self._locale)

    @property
    def locale(self):
        doc = self.doc
        return doc.locale if doc else None

    def list_concrete_paths(self):
        if self.path:
            return [self.path]
        if not self.doc:
            return []
        return [self.doc.get_serving_path()]

    def render(self, params, inject=True):
        doc = self.doc
        preprocessor = None
        translator = None
        if inject:
            preprocessor = self.pod.inject_preprocessors(doc=doc)
            translator = self.pod.inject_translators(doc=doc)
        jinja_env = self.pod.get_jinja_env(self.locale)
        template = jinja_env.get_template(self.view.lstrip('/'))
        track_dependency = doc_dependency.DocDependency(doc)
        local_tags = tags.create_builtin_tags(
            self.pod, doc, track_dependency=track_dependency)
        # NOTE: This should be done using get_template(... globals=...)
        # or passed as an argument into render but
        # it is not available included inside macros???
        # See: https://github.com/pallets/jinja/issues/688
        template.globals['g'] = local_tags

        # Track the message stats, including untranslated strings.
        if self.pod.is_enabled(self.pod.FEATURE_TRANSLATION_STATS):
            template.globals['_'] = tags.make_doc_gettext(doc)

        try:
            kwargs = {
                'doc': doc,
                'env': self.pod.env,
                'podspec': self.pod.podspec,
                '_track_dependency': track_dependency,
            }
            content = template.render(kwargs).lstrip()
            if self.pod.is_enabled(self.pod.FEATURE_UI):
                content = self._inject_ui(
                    content, preprocessor, translator)
            return content
        except Exception as e:
            text = 'Error building {}: {}'
            exception = errors.BuildError(text.format(self, e))
            exception.traceback = sys.exc_info()[2]
            exception.controller = self
            exception.exception = e
            raise exception

    def _inject_ui(self, content, preprocessor, translator):
        if not self.get_mimetype().endswith('html'):
            return content

        ui_settings = self.pod.ui
        show_ui = (self.pod.env.name == env.Name.DEV
                   and (preprocessor or translator))
        if ui_settings or show_ui:
            jinja_env = ui.create_jinja_env()
            ui_template = jinja_env.get_template('ui.html')
            content += '\n' + ui_template.render({
                'doc': self.doc,
                'preprocessor': preprocessor,
                'translator': translator,
                'ui': ui_settings,
            })

        return content
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# (C) British Crown Copyright 2017 Met Office.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
#   contributors may be used to endorse or promote products derived from
#   this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
"""Unit tests for saving functionality."""

import os
import copy
import unittest
import numpy as np
from subprocess import call
from tempfile import mkdtemp

import iris
from iris.tests import IrisTest
from netCDF4 import Dataset

from improver.utilities.load import load_cube
from improver.utilities.save import save_netcdf
from improver.tests.ensemble_calibration.ensemble_calibration.\
    helper_functions import set_up_cube

def set_up_test_cube():
    """ Set up a temperature cube with additional global attributes. """
    data = (np.linspace(-45.0, 45.0, 9).reshape(1, 1, 3, 3) + 273.15)
    cube = set_up_cube(data, 'air_temperature', 'K', realizations=([0]))
    cube.attributes['Conventions'] = 'CF-1.5'
    cube.attributes['source_realizations'] = np.arange(12)
    return cube

class Test_save_netcdf(IrisTest):

    """ Test function to save iris cubes as NetCDF files. """

    def setUp(self):
        """ Set up cube to write, read and check """
        self.global_keys_ref = ['title', 'um_version', 'grid_id', 'source',
                                'Conventions', 'institution', 'history']
        self.directory = mkdtemp()
        self.filepath = os.path.join(self.directory, "temp.nc")
        self.cube = set_up_test_cube()

    def tearDown(self):
        """ Remove temporary directories created for testing. """
        call(['rm', '-f', self.filepath])
        call(['rmdir', self.directory])

    def test_basic_cube(self):
        """ Test saves file in required location """
        self.assertFalse(os.path.exists(self.filepath))
        save_netcdf(self.cube, self.filepath)
        self.assertTrue(os.path.exists(self.filepath))

    def test_basic_cube_list(self):
        """ Test functionality for saving iris.cube.CubeList """
        cube_list = ([self.cube, self.cube])
        save_netcdf(cube_list, self.filepath)
        read_cubes = iris.load(self.filepath)
        self.assertIsInstance(read_cubes, iris.cube.CubeList)
        self.assertEqual(len(read_cubes), 2)

    def test_cube_data(self):
        """ Test valid cube can be read from saved file """
        save_netcdf(self.cube, self.filepath)
        cube = load_cube(self.filepath)
        self.assertTrue(isinstance(cube, iris.cube.Cube))
        self.assertTrue(np.array_equal(cube.data, self.cube.data))

    def test_cube_dimensions(self):
        """ Test cube dimension coordinates are preserved """
        save_netcdf(self.cube, self.filepath)
        cube = load_cube(self.filepath)
        coord_names = [coord.name() for coord in cube.coords(dim_coords=True)]
        reference_names = [coord.name()
                           for coord in self.cube.coords(dim_coords=True)]
        self.assertItemsEqual(coord_names, reference_names)

    def test_cf_global_attributes(self):
        """ Test that a NetCDF file saved from one cube only contains the
        expected global attributes.

        NOTE Loading the file as an iris.cube.Cube does not distinguish global
        from local attributes, and therefore cannot test for the correct
        behaviour here.
        """
        save_netcdf(self.cube, self.filepath)
        global_keys = Dataset(self.filepath, mode='r').ncattrs()
        self.assertTrue(all(key in self.global_keys_ref
                            for key in global_keys))

    def test_cf_data_attributes(self):
        """ Test that forbidden global metadata are saved as data variable
        attributes
        """
        save_netcdf(self.cube, self.filepath)
        temp = Dataset(self.filepath, mode='r').variables['air_temperature']
        self.assertTrue('source_realizations' in temp.ncattrs())
        self.assertTrue(np.array_equal(temp.getncattr('source_realizations'),
                                       np.arange(12)))

    def test_cf_shared_attributes_list(self):
        """ Test that a NetCDF file saved from a list of cubes that share
        non-global attributes does not promote these attributes to global.
        """
        cube_list = ([self.cube, self.cube])
        save_netcdf(cube_list, self.filepath)
        global_keys = Dataset(self.filepath, mode='r').ncattrs()
        self.assertEqual(len(global_keys), 1)
        self.assertTrue(all(key in self.global_keys_ref
                            for key in global_keys))

if __name__ == '__main__':
    unittest.main()
<commit_msg>Removed unused import.<commit_after># -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# (C) British Crown Copyright 2017 Met Office.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
#   contributors may be used to endorse or promote products derived from
#   this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
"""Unit tests for saving functionality."""

import os
import unittest
import numpy as np
from subprocess import call
from tempfile import mkdtemp

import iris
from iris.tests import IrisTest
from netCDF4 import Dataset

from improver.utilities.load import load_cube
from improver.utilities.save import save_netcdf
from improver.tests.ensemble_calibration.ensemble_calibration.\
    helper_functions import set_up_cube

def set_up_test_cube():
    """ Set up a temperature cube with additional global attributes. """
    data = (np.linspace(-45.0, 45.0, 9).reshape(1, 1, 3, 3) + 273.15)
    cube = set_up_cube(data, 'air_temperature', 'K', realizations=([0]))
    cube.attributes['Conventions'] = 'CF-1.5'
    cube.attributes['source_realizations'] = np.arange(12)
    return cube

class Test_save_netcdf(IrisTest):

    """ Test function to save iris cubes as NetCDF files. """

    def setUp(self):
        """ Set up cube to write, read and check """
        self.global_keys_ref = ['title', 'um_version', 'grid_id', 'source',
                                'Conventions', 'institution', 'history']
        self.directory = mkdtemp()
        self.filepath = os.path.join(self.directory, "temp.nc")
        self.cube = set_up_test_cube()

    def tearDown(self):
        """ Remove temporary directories created for testing. """
        call(['rm', '-f', self.filepath])
        call(['rmdir', self.directory])

    def test_basic_cube(self):
        """ Test saves file in required location """
        self.assertFalse(os.path.exists(self.filepath))
        save_netcdf(self.cube, self.filepath)
        self.assertTrue(os.path.exists(self.filepath))

    def test_basic_cube_list(self):
        """ Test functionality for saving iris.cube.CubeList """
        cube_list = ([self.cube, self.cube])
        save_netcdf(cube_list, self.filepath)
        read_cubes = iris.load(self.filepath)
        self.assertIsInstance(read_cubes, iris.cube.CubeList)
        self.assertEqual(len(read_cubes), 2)

    def test_cube_data(self):
        """ Test valid cube can be read from saved file """
        save_netcdf(self.cube, self.filepath)
        cube = load_cube(self.filepath)
        self.assertTrue(isinstance(cube, iris.cube.Cube))
        self.assertTrue(np.array_equal(cube.data, self.cube.data))

    def test_cube_dimensions(self):
        """ Test cube dimension coordinates are preserved """
        save_netcdf(self.cube, self.filepath)
        cube = load_cube(self.filepath)
        coord_names = [coord.name() for coord in cube.coords(dim_coords=True)]
        reference_names = [coord.name()
                           for coord in self.cube.coords(dim_coords=True)]
        self.assertItemsEqual(coord_names, reference_names)

    def test_cf_global_attributes(self):
        """ Test that a NetCDF file saved from one cube only contains the
        expected global attributes.

        NOTE Loading the file as an iris.cube.Cube does not distinguish global
        from local attributes, and therefore cannot test for the correct
        behaviour here.
        """
        save_netcdf(self.cube, self.filepath)
        global_keys = Dataset(self.filepath, mode='r').ncattrs()
        self.assertTrue(all(key in self.global_keys_ref
                            for key in global_keys))

    def test_cf_data_attributes(self):
        """ Test that forbidden global metadata are saved as data variable
        attributes
        """
        save_netcdf(self.cube, self.filepath)
        temp = Dataset(self.filepath, mode='r').variables['air_temperature']
        self.assertTrue('source_realizations' in temp.ncattrs())
        self.assertTrue(np.array_equal(temp.getncattr('source_realizations'),
                                       np.arange(12)))

    def test_cf_shared_attributes_list(self):
        """ Test that a NetCDF file saved from a list of cubes that share
        non-global attributes does not promote these attributes to global.
        """
        cube_list = ([self.cube, self.cube])
        save_netcdf(cube_list, self.filepath)
        global_keys = Dataset(self.filepath, mode='r').ncattrs()
        self.assertEqual(len(global_keys), 1)
        self.assertTrue(all(key in self.global_keys_ref
                            for key in global_keys))

if __name__ == '__main__':
    unittest.main()
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
from django import forms
from django.forms import ModelForm, models, formsets
from django.contrib.admin.widgets import AdminDateWidget
from dailyreport.models import Daily, Comment, Task

class DailyForm(ModelForm):
    u"""Dailyモデル編集用フォーム
        Dailyモデルを編集するためのフォームです
        編集可能な項目
            title:          タイトル
            create_date:    投稿日
            report_y:       やったこと
            report_w:       わかったこと
            report_t:       つぎにやること
        タイトルと作成日について
            現在の"【日報】0830チーム開発演習デザイン作成"というようなタイトルを例にとります
                タイトル：「チーム開発演習デザイン作成」
                投稿日日：0830
            という風に対応します
            また、これは日報管理アプリであるので、「日報」であることは自明であるとします
        投稿日について
            投稿日(create_date)はカレンダーウィジェットから選択することができます
            文字列で入力する場合は"YYYY-MM-DD"という形式です
    """
    class Meta:
        model = Daily
        fields = ('title', 'create_date', 'report_y', 'report_w', 'report_t', )
        widgets = {
            'create_date': AdminDateWidget(attrs={'placeholder': 'YYYY-MM-DD'}),
            'title': forms.TextInput(attrs={
                'class': 'daily_title_form',
                'placeholder': '日報のタイトル',
                'required': ''}),
            'report_y': forms.Textarea(attrs={
                'class': 'report_y_form',
                'placeholder': 'タスクの完了状態についてコメントしましょう'}),
            'report_w': forms.Textarea(attrs={
                'class': 'report_w_form',
                'placeholder': '一日の作業を通してわかったことやできたことを書いてください'}),
            'report_t': forms.Textarea(attrs={
                'class': 'report_t_form',
                'placeholder': '明日のタスクで気を付けることがあったらコメントしましょう'}),
        }

    def clean(self):
        cleaned_data = self.cleaned_data
        try:
            title = cleaned_data['title']
        except:
            raise forms.ValidationError('タイトルを入力してください')
        if len(title.strip()) < 1:
            raise forms.ValidationError('空白や改行を除き、1文字以上入力してください')
        return cleaned_data

class TaskForm(ModelForm):
    u"""Taskモデル編集用フォーム
        Taskモデルを編集するためのフォームです
        編集可能な項目
            complete_task:  タスクの完了状態
            implement_date: タスクの実施日
            name:           タスク名
            time_plan:      予定作業時間
            time_real:      実際の作業時間
        実施日はカレンダーウィジェットから選択することができます
        文字列で入力する場合は"YYYY-MM-DD"という形式です
    """
    class Meta:
        model = Task
        fields = ('complete_task', 'implement_date', 'name', 'time_plan', 'time_real', )
        widgets = {
            'implement_date': AdminDateWidget(attrs={'placeholder': 'YYYY-MM-DD', 'required': ''}),
            'name': forms.TextInput(attrs={'placeholder': '作業概要', 'required': ''}),
            'time_plan': forms.TextInput(attrs={
                'class': 'time_plan', 'type': 'number', 'placeholder': '0', 'required': ''}),
            'time_real': forms.TextInput(attrs={'class': 'time_real', 'type': 'number'}),
        }

    def clean(self):
        cleaned_data = self.cleaned_data
        try:
            name = cleaned_data['name']
        except KeyError:
            raise forms.ValidationError('タスク名を入力してください')
        try:
            implement_date = cleaned_data['implement_date']
        except KeyError:
            raise forms.ValidationError('作業予定日を入力してください')
        try:
            time_plan = cleaned_data['time_plan']
        except KeyError:
            raise forms.ValidationError('予定作業時間数を入力してください')
        try:
            time_real = cleaned_data['time_real']
        except KeyError:
            raise forms.ValidationError('実作業時間数を入力してください')
        if time_plan < 0 or time_real < 0:
            raise forms.ValidationError('負の値は時間数として入力できません')
        if time_plan < 1:
            raise forms.ValidationError('予定作業時間には1以上の時間を指定してください')
        if cleaned_data['complete_task'] and time_real is 0:
            raise forms.ValidationError('実作業時間を入力してください')

        return cleaned_data

class CommentForm(ModelForm):
    u"""Commentモデル編集用フォーム
        Commentモデルを編集するためのフォームです
        編集可能な項目
            comment:    コメント本文

    """
    class Meta:
        model = Comment
        fields = ('comment', )
        widgets = {
            'comment': forms.Textarea(attrs={'class': 'comment_form', 'required': ''},)
        }

    def clean(self):
        cleaned_data = self.cleaned_data
        try:
            comment = cleaned_data['comment']
        except:
            raise forms.ValidationError('コメントを入力してください')
        if len(comment.strip()) < 1:
            raise forms.ValidationError('空白や改行を除き、1文字以上入力してください')
        return cleaned_data

class SearchForm(forms.Form):
    u"""キーワード検索用入力フォーム
        キーワード検索用のキーワード入力フォームです
    """
    keyword = forms.CharField(max_length=100, required=False,
                              widget=forms.TextInput(attrs={
                                  'class': 'form-control',
                                  'placeholder': 'キーワード',
                                  'required': ''}))

    class Meta:
        pass

class DateForm(forms.Form):
    u"""日付絞込み用入力フォーム
        日付絞込み用の日付入力フォームです
        ウィジェットを登録していますので、カレンダーから日付を選択することもできます
    """
    date = forms.DateField(widget=AdminDateWidget(attrs={'placeholder': 'YYYY-MM-DD'}))

class DailySearchForm(forms.Form):
    u"""投稿状態絞り込み用入力フォーム
        日報のレコードを絞り込むためのフォームです
        日報の状態をドロップダウンリストにより、"すべて"、"公開"、"未公開"から選択することができます
    """

    cond = forms.ChoiceField(
        choices=[("0", "すべて表示"), ("1", "公開"), ("2", "未公開")])

    def clean(self):
        cleaned_data = self.cleaned_data
        try:
            cond = cleaned_data['cond']
        except KeyError:
            cleaned_data.update(cond=0)
        return cleaned_data

class TaskSearchForm(forms.Form):
    u"""日付絞込み/タスク状態絞り込み用入力フォーム
        タスクのレコードを絞り込むためのフォームです
        ウィジェットを登録していますので、カレンダーから日付を選択することもできます
        また、タスクの完了状態をドロップダウンリストにより、"すべて"、"完了"、"未完了"から選択することができます
    """
    date = forms.DateField(widget=AdminDateWidget(attrs={'placeholder': 'YYYY-MM-DD'}))
    cond = forms.ChoiceField(
        choices=[("0", "すべて表示"), ("1", "完了したタスク"), ("2", "未完了のタスク")])

    def clean(self):
        cleaned_data = self.cleaned_data
        try:
            cond = cleaned_data['cond']
        except KeyError:
            cleaned_data.update(cond=0)
        return cleaned_data

u"""タスク入力/編集用フォームセット
    タスクモデルのレコード複数を一括で更新/追加するためのフォームです
    タスクレコードの表示数は、表示対象となるクエリセット数(フォーム作成時に指定)と等しく、
    それとは別に新規入力用に空のフォームを一つ持ちます

    ※以下、TaskFormSet.modelの指定までが定義です
"""
TaskFormSet = formsets.formset_factory(TaskForm, extra=1, formset=models.BaseModelFormSet, can_delete=True)
TaskFormSet.model = Task

<commit_msg>[fix]remove required option in task form<commit_after># -*- coding: utf-8 -*-
from django import forms
from django.forms import ModelForm, models, formsets
from django.contrib.admin.widgets import AdminDateWidget
from dailyreport.models import Daily, Comment, Task

class DailyForm(ModelForm):
    u"""Dailyモデル編集用フォーム
        Dailyモデルを編集するためのフォームです
        編集可能な項目
            title:          タイトル
            create_date:    投稿日
            report_y:       やったこと
            report_w:       わかったこと
            report_t:       つぎにやること
        タイトルと作成日について
            現在の"【日報】0830チーム開発演習デザイン作成"というようなタイトルを例にとります
                タイトル：「チーム開発演習デザイン作成」
                投稿日日：0830
            という風に対応します
            また、これは日報管理アプリであるので、「日報」であることは自明であるとします
        投稿日について
            投稿日(create_date)はカレンダーウィジェットから選択することができます
            文字列で入力する場合は"YYYY-MM-DD"という形式です
    """
    class Meta:
        model = Daily
        fields = ('title', 'create_date', 'report_y', 'report_w', 'report_t', )
        widgets = {
            'create_date': AdminDateWidget(attrs={'placeholder': 'YYYY-MM-DD'}),
            'title': forms.TextInput(attrs={
                'class': 'daily_title_form',
                'placeholder': '日報のタイトル',
                'required': ''}),
            'report_y': forms.Textarea(attrs={
                'class': 'report_y_form',
                'placeholder': 'タスクの完了状態についてコメントしましょう'}),
            'report_w': forms.Textarea(attrs={
                'class': 'report_w_form',
                'placeholder': '一日の作業を通してわかったことやできたことを書いてください'}),
            'report_t': forms.Textarea(attrs={
                'class': 'report_t_form',
                'placeholder': '明日のタスクで気を付けることがあったらコメントしましょう'}),
        }

    def clean(self):
        cleaned_data = self.cleaned_data
        try:
            title = cleaned_data['title']
        except:
            raise forms.ValidationError('タイトルを入力してください')
        if len(title.strip()) < 1:
            raise forms.ValidationError('空白や改行を除き、1文字以上入力してください')
        return cleaned_data

class TaskForm(ModelForm):
    u"""Taskモデル編集用フォーム
        Taskモデルを編集するためのフォームです
        編集可能な項目
            complete_task:  タスクの完了状態
            implement_date: タスクの実施日
            name:           タスク名
            time_plan:      予定作業時間
            time_real:      実際の作業時間
        実施日はカレンダーウィジェットから選択することができます
        文字列で入力する場合は"YYYY-MM-DD"という形式です
    """
    class Meta:
        model = Task
        fields = ('complete_task', 'implement_date', 'name', 'time_plan', 'time_real', )
        widgets = {
            'implement_date': AdminDateWidget(attrs={'placeholder': 'YYYY-MM-DD'}),
            'name': forms.TextInput(attrs={'placeholder': '作業概要'}),
            'time_plan': forms.TextInput(attrs={
                'class': 'time_plan', 'type': 'number', 'placeholder': '0'}),
            'time_real': forms.TextInput(attrs={'class': 'time_real', 'type': 'number'}),
        }

    def clean(self):
        cleaned_data = self.cleaned_data
        try:
            name = cleaned_data['name']
        except KeyError:
            raise forms.ValidationError('タスク名を入力してください')
        try:
            implement_date = cleaned_data['implement_date']
        except KeyError:
            raise forms.ValidationError('作業予定日を入力してください')
        try:
            time_plan = cleaned_data['time_plan']
        except KeyError:
            raise forms.ValidationError('予定作業時間数を入力してください')
        try:
            time_real = cleaned_data['time_real']
        except KeyError:
            raise forms.ValidationError('実作業時間数を入力してください')
        if time_plan < 0 or time_real < 0:
            raise forms.ValidationError('負の値は時間数として入力できません')
        if time_plan < 1:
            raise forms.ValidationError('予定作業時間には1以上の時間を指定してください')
        if cleaned_data['complete_task'] and time_real is 0:
            raise forms.ValidationError('実作業時間を入力してください')

        return cleaned_data

class CommentForm(ModelForm):
    u"""Commentモデル編集用フォーム
        Commentモデルを編集するためのフォームです
        編集可能な項目
            comment:    コメント本文

    """
    class Meta:
        model = Comment
        fields = ('comment', )
        widgets = {
            'comment': forms.Textarea(attrs={'class': 'comment_form', 'required': ''},)
        }

    def clean(self):
        cleaned_data = self.cleaned_data
        try:
            comment = cleaned_data['comment']
        except:
            raise forms.ValidationError('コメントを入力してください')
        if len(comment.strip()) < 1:
            raise forms.ValidationError('空白や改行を除き、1文字以上入力してください')
        return cleaned_data

class SearchForm(forms.Form):
    u"""キーワード検索用入力フォーム
        キーワード検索用のキーワード入力フォームです
    """
    keyword = forms.CharField(max_length=100, required=False,
                              widget=forms.TextInput(attrs={
                                  'class': 'form-control',
                                  'placeholder': 'キーワード',
                                  'required': ''}))

    class Meta:
        pass

class DateForm(forms.Form):
    u"""日付絞込み用入力フォーム
        日付絞込み用の日付入力フォームです
        ウィジェットを登録していますので、カレンダーから日付を選択することもできます
    """
    date = forms.DateField(widget=AdminDateWidget(attrs={'placeholder': 'YYYY-MM-DD'}))

class DailySearchForm(forms.Form):
    u"""投稿状態絞り込み用入力フォーム
        日報のレコードを絞り込むためのフォームです
        日報の状態をドロップダウンリストにより、"すべて"、"公開"、"未公開"から選択することができます
    """

    cond = forms.ChoiceField(
        choices=[("0", "すべて表示"), ("1", "公開"), ("2", "未公開")])

    def clean(self):
        cleaned_data = self.cleaned_data
        try:
            cond = cleaned_data['cond']
        except KeyError:
            cleaned_data.update(cond=0)
        return cleaned_data

class TaskSearchForm(forms.Form):
    u"""日付絞込み/タスク状態絞り込み用入力フォーム
        タスクのレコードを絞り込むためのフォームです
        ウィジェットを登録していますので、カレンダーから日付を選択することもできます
        また、タスクの完了状態をドロップダウンリストにより、"すべて"、"完了"、"未完了"から選択することができます
    """
    date = forms.DateField(widget=AdminDateWidget(attrs={'placeholder': 'YYYY-MM-DD'}))
    cond = forms.ChoiceField(
        choices=[("0", "すべて表示"), ("1", "完了したタスク"), ("2", "未完了のタスク")])

    def clean(self):
        cleaned_data = self.cleaned_data
        try:
            cond = cleaned_data['cond']
        except KeyError:
            cleaned_data.update(cond=0)
        return cleaned_data

u"""タスク入力/編集用フォームセット
    タスクモデルのレコード複数を一括で更新/追加するためのフォームです
    タスクレコードの表示数は、表示対象となるクエリセット数(フォーム作成時に指定)と等しく、
    それとは別に新規入力用に空のフォームを一つ持ちます

    ※以下、TaskFormSet.modelの指定までが定義です
"""
TaskFormSet = formsets.formset_factory(TaskForm, extra=1, formset=models.BaseModelFormSet, can_delete=True)
TaskFormSet.model = Task

<|endoftext|>


<commit_before># Copyright (c) Facebook, Inc. and its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import print_function

import errno
import hashlib
import logging
import os
import shutil
import subprocess
import sys
import tempfile
import textwrap

from programs import file_locks
from programs.tracing import Tracing

def get_file_contents_if_exists(path, default=None):
    with Tracing("BuckProject.get_file_contents_if_it_exists", args={"path": path}):
        if not os.path.exists(path):
            return default
        with open(path) as f:
            contents = f.read().strip()
            return default if not contents else contents

def write_contents_to_file(path, contents):
    with Tracing("BuckProject.write_contents_to_file", args={"path": path}):
        with open(path, "w") as output_file:
            output_file.write(str(contents))

def makedirs(path):
    try:
        os.makedirs(path)
    except OSError as e:
        # Potentially the case that multiple processes are running in parallel
        # (e.g. a series of linters running buck query without buckd), so we
        # should just swallow the error.
        # This is mostly equivalent to os.makedirs(path, exist_ok=True) in
        # Python 3.
        if e.errno == errno.EEXIST and os.path.isdir(path):
            return
        raise

def _is_eden(path):
    return os.path.isdir(os.path.join(path, ".eden", "root"))

def _find_eden_root(project_root):
    return os.readlink(os.path.join(project_root, ".eden", "root"))

def _add_eden_bindmount(eden_root, path):
    relative_path = os.path.relpath(path, eden_root)
    logging.debug(
        "Adding eden mount at {}, path relative to eden {}".format(path, relative_path)
    )
    try:
        subprocess.check_output(["eden", "redirect", "add", relative_path, "bind"])
    except subprocess.CalledProcessError:
        logging.warning("Could not add eden redirect for " + path)
        raise

def add_eden_bindmounts(repo_root, buck_out):
    if not _is_eden(repo_root):
        return

    add_bindmounts = os.environ.get("NO_BUCK_ADD_EDEN_BINDMOUNTS", "0").strip() == "0"
    if not add_bindmounts:
        logging.warning(
            "Skipping adding eden bindmounts because "
            + "NO_BUCK_ADD_EDEN_BINDMOUNTS was set"
        )
        return

    eden_root = _find_eden_root(repo_root)

    eden_bindmounts = set(buck_out)
    eden_bindmounts_file = os.path.join(repo_root, ".buck-eden-bindmounts")
    if os.path.exists(eden_bindmounts_file):
        logging.debug("Reading eden bindmounts from " + eden_bindmounts_file)
        with open(eden_bindmounts_file, "r") as fin:
            for bindmount in fin:
                bindmount = bindmount.strip()
                if bindmount and not bindmount.startswith("#"):
                    eden_bindmounts.add(os.path.join(repo_root, bindmount))

    for mount in eden_bindmounts:
        if os.path.exists(mount):
            if _is_eden(mount):
                msg = (
                    "Eden bindmount at {path} was requested, but it is already a "
                    "directory within an eden filesystem.\n"
                    "In order to prevent destructive actions on user data, you "
                    "must remove this directory yourself.\n"
                    "Please stop buck with `buck killall`, remove {path}, and run buck "
                    "again."
                )
                logging.warning(msg.format(path=mount))
            else:
                logging.debug(
                    "Eden bindmount at {} already exists, skipping".format(mount)
                )
        else:
            _add_eden_bindmount(eden_root, mount)

class BuckProject:
    def __init__(self, root):
        self.root = root
        try:
            isolated_pos = sys.argv.index("--isolation_prefix")
            if isolated_pos < len(sys.argv):
                self.prefix = sys.argv[isolated_pos + 1]
            else:
                self.prefix = ""
        except ValueError:
            self.prefix = ""

        self._buck_out_dirname = "buck-out"
        if len(self.prefix) > 0:
            self._buck_out_dirname = self.prefix + "-" + self._buck_out_dirname

        self._buck_out = os.path.join(self.root, self._buck_out_dirname)
        add_eden_bindmounts(self.root, self._buck_out)

        self._buck_out_tmp = os.path.join(self._buck_out, "tmp")
        makedirs(self._buck_out_tmp)
        self._buck_out_log = os.path.join(self._buck_out, "log")
        makedirs(self._buck_out_log)
        self.tmp_dir = tempfile.mkdtemp(prefix="buck_run.", dir=self._buck_out_tmp)

        # Only created if buckd is used.
        self.buckd_tmp_dir = None

        self.buckd_dir = os.path.join(root, self.prefix + ".buckd")
        self.buckd_version_file = os.path.join(self.buckd_dir, "buckd.version")
        self.buckd_pid_file = os.path.join(self.buckd_dir, "pid")
        self.buckd_stdout = os.path.join(self.buckd_dir, "stdout")
        self.buckd_stderr = os.path.join(self.buckd_dir, "stderr")
        self.buckd_jvm_args_file = os.path.join(self.buckd_dir, "buckjavaargs.running")

        buck_javaargs_path = os.path.join(self.root, ".buckjavaargs")
        self.buck_javaargs = get_file_contents_if_exists(buck_javaargs_path)

        buck_javaargs_path_local = os.path.join(self.root, ".buckjavaargs.local")
        self.buck_javaargs_local = get_file_contents_if_exists(buck_javaargs_path_local)

    def get_root_hash(self):
        return hashlib.sha256(self.root.encode("utf-8")).hexdigest()

    def get_buckd_transport_file_path(self):
        if os.name == "nt":
            return u"\\\\.\\pipe\\buckd_{0}".format(self.get_root_hash())
        else:
            return os.path.join(self.buckd_dir, "sock")

    def get_buckd_transport_address(self):
        if os.name == "nt":
            return "local:buckd_{0}".format(self.get_root_hash())
        else:
            return "local:{0}.buckd/sock".format(self.prefix)

    def get_running_buckd_version(self):
        return get_file_contents_if_exists(self.buckd_version_file)

    def get_running_buckd_pid(self):
        try:
            return int(get_file_contents_if_exists(self.buckd_pid_file))
        except ValueError:
            return None
        except TypeError:
            return None

    def get_running_buckd_jvm_args(self):
        args_string = get_file_contents_if_exists(self.buckd_jvm_args_file)
        return args_string.split("\n") if args_string is not None else []

    def get_buckd_stdout(self):
        return self.buckd_stdout

    def get_buckd_stderr(self):
        return self.buckd_stderr

    def get_buck_out_log_dir(self):
        return self._buck_out_log

    def get_buck_out_relative_dir(self):
        return self._buck_out_dirname

    def clean_up_buckd(self):
        with Tracing("BuckProject.clean_up_buckd"):
            if os.path.exists(self.buckd_dir):
                file_locks.rmtree_if_can_lock(self.buckd_dir)

    def create_buckd_dir(self):
        makedirs(self.buckd_dir)

    def create_buckd_tmp_dir(self):
        if self.buckd_tmp_dir is not None:
            return self.buckd_tmp_dir
        self.buckd_tmp_dir = tempfile.mkdtemp(
            prefix="buckd_tmp.", dir=self._buck_out_tmp
        )
        return self.buckd_tmp_dir

    def save_buckd_version(self, version):
        write_contents_to_file(self.buckd_version_file, version)

    def save_buckd_pid(self, pid):
        write_contents_to_file(self.buckd_pid_file, str(pid))

    def save_buckd_jvm_args(self, args):
        write_contents_to_file(self.buckd_jvm_args_file, "\n".join(args))

    @staticmethod
    def from_current_dir():
        with Tracing("BuckProject.from_current_dir"):
            current_dir = os.getcwd()
            if "--version" in sys.argv or "-V" in sys.argv:
                return BuckProject(current_dir)
            at_root_dir = False
            while not at_root_dir:
                if os.path.exists(os.path.join(current_dir, ".buckconfig")):
                    return BuckProject(current_dir)
                parent_dir = os.path.dirname(current_dir)
                at_root_dir = current_dir == parent_dir
                current_dir = parent_dir
            raise NoBuckConfigFoundException()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        with Tracing("BuckProject.__exit__"):
            if os.path.exists(self.tmp_dir):
                try:
                    shutil.rmtree(self.tmp_dir)
                except OSError as e:
                    if e.errno != errno.ENOENT:
                        raise

class NoBuckConfigFoundException(Exception):
    def __init__(self):
        no_buckconfig_message_path = ".no_buckconfig_message"
        default_message = textwrap.dedent(
            """\
            This does not appear to be the root of a Buck project. Please 'cd'
            to the root of your project before running buck. If this really is
            the root of your project, run
            'touch .buckconfig'
            and then re-run your buck command."""
        )
        message = get_file_contents_if_exists(
            no_buckconfig_message_path, default_message
        )
        Exception.__init__(self, message)
<commit_msg>Fix set() usage in buck_project.py<commit_after># Copyright (c) Facebook, Inc. and its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import print_function

import errno
import hashlib
import logging
import os
import shutil
import subprocess
import sys
import tempfile
import textwrap

from programs import file_locks
from programs.tracing import Tracing

def get_file_contents_if_exists(path, default=None):
    with Tracing("BuckProject.get_file_contents_if_it_exists", args={"path": path}):
        if not os.path.exists(path):
            return default
        with open(path) as f:
            contents = f.read().strip()
            return default if not contents else contents

def write_contents_to_file(path, contents):
    with Tracing("BuckProject.write_contents_to_file", args={"path": path}):
        with open(path, "w") as output_file:
            output_file.write(str(contents))

def makedirs(path):
    try:
        os.makedirs(path)
    except OSError as e:
        # Potentially the case that multiple processes are running in parallel
        # (e.g. a series of linters running buck query without buckd), so we
        # should just swallow the error.
        # This is mostly equivalent to os.makedirs(path, exist_ok=True) in
        # Python 3.
        if e.errno == errno.EEXIST and os.path.isdir(path):
            return
        raise

def _is_eden(path):
    return os.path.isdir(os.path.join(path, ".eden", "root"))

def _find_eden_root(project_root):
    return os.readlink(os.path.join(project_root, ".eden", "root"))

def _add_eden_bindmount(eden_root, path):
    relative_path = os.path.relpath(path, eden_root)
    logging.debug(
        "Adding eden mount at {}, path relative to eden {}".format(path, relative_path)
    )
    try:
        subprocess.check_output(["eden", "redirect", "add", relative_path, "bind"])
    except subprocess.CalledProcessError:
        logging.warning("Could not add eden redirect for " + path)
        raise

def add_eden_bindmounts(repo_root, buck_out):
    if not _is_eden(repo_root):
        return

    add_bindmounts = os.environ.get("NO_BUCK_ADD_EDEN_BINDMOUNTS", "0").strip() == "0"
    if not add_bindmounts:
        logging.warning(
            "Skipping adding eden bindmounts because "
            + "NO_BUCK_ADD_EDEN_BINDMOUNTS was set"
        )
        return

    eden_root = _find_eden_root(repo_root)

    eden_bindmounts = {buck_out}
    eden_bindmounts_file = os.path.join(repo_root, ".buck-eden-bindmounts")
    if os.path.exists(eden_bindmounts_file):
        logging.debug("Reading eden bindmounts from " + eden_bindmounts_file)
        with open(eden_bindmounts_file, "r") as fin:
            for bindmount in fin:
                bindmount = bindmount.strip()
                if bindmount and not bindmount.startswith("#"):
                    eden_bindmounts.add(os.path.join(repo_root, bindmount))

    for mount in eden_bindmounts:
        if os.path.exists(mount):
            if _is_eden(mount):
                msg = (
                    "Eden bindmount at {path} was requested, but it is already a "
                    "directory within an eden filesystem.\n"
                    "In order to prevent destructive actions on user data, you "
                    "must remove this directory yourself.\n"
                    "Please stop buck with `buck killall`, remove {path}, and run buck "
                    "again."
                )
                logging.warning(msg.format(path=mount))
            else:
                logging.debug(
                    "Eden bindmount at {} already exists, skipping".format(mount)
                )
        else:
            _add_eden_bindmount(eden_root, mount)

class BuckProject:
    def __init__(self, root):
        self.root = root
        try:
            isolated_pos = sys.argv.index("--isolation_prefix")
            if isolated_pos < len(sys.argv):
                self.prefix = sys.argv[isolated_pos + 1]
            else:
                self.prefix = ""
        except ValueError:
            self.prefix = ""

        self._buck_out_dirname = "buck-out"
        if len(self.prefix) > 0:
            self._buck_out_dirname = self.prefix + "-" + self._buck_out_dirname

        self._buck_out = os.path.join(self.root, self._buck_out_dirname)
        add_eden_bindmounts(self.root, self._buck_out)

        self._buck_out_tmp = os.path.join(self._buck_out, "tmp")
        makedirs(self._buck_out_tmp)
        self._buck_out_log = os.path.join(self._buck_out, "log")
        makedirs(self._buck_out_log)
        self.tmp_dir = tempfile.mkdtemp(prefix="buck_run.", dir=self._buck_out_tmp)

        # Only created if buckd is used.
        self.buckd_tmp_dir = None

        self.buckd_dir = os.path.join(root, self.prefix + ".buckd")
        self.buckd_version_file = os.path.join(self.buckd_dir, "buckd.version")
        self.buckd_pid_file = os.path.join(self.buckd_dir, "pid")
        self.buckd_stdout = os.path.join(self.buckd_dir, "stdout")
        self.buckd_stderr = os.path.join(self.buckd_dir, "stderr")
        self.buckd_jvm_args_file = os.path.join(self.buckd_dir, "buckjavaargs.running")

        buck_javaargs_path = os.path.join(self.root, ".buckjavaargs")
        self.buck_javaargs = get_file_contents_if_exists(buck_javaargs_path)

        buck_javaargs_path_local = os.path.join(self.root, ".buckjavaargs.local")
        self.buck_javaargs_local = get_file_contents_if_exists(buck_javaargs_path_local)

    def get_root_hash(self):
        return hashlib.sha256(self.root.encode("utf-8")).hexdigest()

    def get_buckd_transport_file_path(self):
        if os.name == "nt":
            return u"\\\\.\\pipe\\buckd_{0}".format(self.get_root_hash())
        else:
            return os.path.join(self.buckd_dir, "sock")

    def get_buckd_transport_address(self):
        if os.name == "nt":
            return "local:buckd_{0}".format(self.get_root_hash())
        else:
            return "local:{0}.buckd/sock".format(self.prefix)

    def get_running_buckd_version(self):
        return get_file_contents_if_exists(self.buckd_version_file)

    def get_running_buckd_pid(self):
        try:
            return int(get_file_contents_if_exists(self.buckd_pid_file))
        except ValueError:
            return None
        except TypeError:
            return None

    def get_running_buckd_jvm_args(self):
        args_string = get_file_contents_if_exists(self.buckd_jvm_args_file)
        return args_string.split("\n") if args_string is not None else []

    def get_buckd_stdout(self):
        return self.buckd_stdout

    def get_buckd_stderr(self):
        return self.buckd_stderr

    def get_buck_out_log_dir(self):
        return self._buck_out_log

    def get_buck_out_relative_dir(self):
        return self._buck_out_dirname

    def clean_up_buckd(self):
        with Tracing("BuckProject.clean_up_buckd"):
            if os.path.exists(self.buckd_dir):
                file_locks.rmtree_if_can_lock(self.buckd_dir)

    def create_buckd_dir(self):
        makedirs(self.buckd_dir)

    def create_buckd_tmp_dir(self):
        if self.buckd_tmp_dir is not None:
            return self.buckd_tmp_dir
        self.buckd_tmp_dir = tempfile.mkdtemp(
            prefix="buckd_tmp.", dir=self._buck_out_tmp
        )
        return self.buckd_tmp_dir

    def save_buckd_version(self, version):
        write_contents_to_file(self.buckd_version_file, version)

    def save_buckd_pid(self, pid):
        write_contents_to_file(self.buckd_pid_file, str(pid))

    def save_buckd_jvm_args(self, args):
        write_contents_to_file(self.buckd_jvm_args_file, "\n".join(args))

    @staticmethod
    def from_current_dir():
        with Tracing("BuckProject.from_current_dir"):
            current_dir = os.getcwd()
            if "--version" in sys.argv or "-V" in sys.argv:
                return BuckProject(current_dir)
            at_root_dir = False
            while not at_root_dir:
                if os.path.exists(os.path.join(current_dir, ".buckconfig")):
                    return BuckProject(current_dir)
                parent_dir = os.path.dirname(current_dir)
                at_root_dir = current_dir == parent_dir
                current_dir = parent_dir
            raise NoBuckConfigFoundException()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        with Tracing("BuckProject.__exit__"):
            if os.path.exists(self.tmp_dir):
                try:
                    shutil.rmtree(self.tmp_dir)
                except OSError as e:
                    if e.errno != errno.ENOENT:
                        raise

class NoBuckConfigFoundException(Exception):
    def __init__(self):
        no_buckconfig_message_path = ".no_buckconfig_message"
        default_message = textwrap.dedent(
            """\
            This does not appear to be the root of a Buck project. Please 'cd'
            to the root of your project before running buck. If this really is
            the root of your project, run
            'touch .buckconfig'
            and then re-run your buck command."""
        )
        message = get_file_contents_if_exists(
            no_buckconfig_message_path, default_message
        )
        Exception.__init__(self, message)
<|endoftext|>