<commit_before>from threeML.plugin_prototype import PluginPrototype

from threeML.models.Parameter import Parameter

from threeML.io.fileUtils import fileExistingAndReadable, sanitizeFilename

from threeML.pyModelInterface import pyToCppModelInterface

from hawc import liff_3ML

import os, sys, collections

import matplotlib.pyplot as plt
from matplotlib import gridspec

import numpy

defaultMinChannel = 0
defaultMaxChannel = 9

__instrument_name = "HAWC"

class HAWCLike( PluginPrototype ):

    def __init__( self, name, maptree, response, ntransits, **kwargs ):

        #This controls if the likeHAWC class should load the entire
        #map or just a small disc around a source (faster).
        #Default is the latter, which is way faster. LIFF will decide
        #autonomously which ROI to use depending on the source model

        self.fullsky = False

        if 'fullsky' in kwargs.keys():

            self.fullsky = bool( kwargs['fullsky'] )

        self.name = str( name )

        #Sanitize files in input (expand variables and so on)

        self.maptree = os.path.abspath( sanitizeFilename( maptree ) )

        self.response = os.path.abspath( sanitizeFilename( response ) )

        #
        self.ntransits = ntransits

        #Check that they exists and can be read

        if not fileExistingAndReadable( self.maptree ):

            raise IOError("MapTree %s does not exist or is not readable" % maptree)

        if not fileExistingAndReadable( self.response ):

            raise IOError("Response %s does not exist or is not readable" % response)

        #Post-pone the creation of the LIFF instance to when
        #we have the likelihood model

        self.instanced = False

        #Default value for minChannel and maxChannel

        self.minChannel = int( defaultMinChannel )
        self.maxChannel = int( defaultMaxChannel )

        #By default the fit of the CommonNorm is deactivated

        self.deactivateCommonNorm()

        #This is to keep track of whether the user defined a ROI or not

        self.roi_ra = None

        #Further setup

        self.__setup()

    def setROI(self, ra, dec, radius, fixedROI=False):

        self.roi_ra = ra
        self.roi_dec = dec

        self.roi_radius = radius

        self.fixedROI = fixedROI

    def __setup(self):

        #I put this here so I can use it both from the __init__ both from
        #the __setstate__ methods

        #Create the dictionary of nuisance parameters

        self.nuisanceParameters = collections.OrderedDict()
        self.nuisanceParameters['CommonNorm'] = Parameter("CommonNorm",1.0,0.5,1.5,0.01,
                                                           fixed=True,nuisance=True)

    def __getstate__(self):

        #This method is used by pickle before attempting to pickle the class

        #Return only the objects needed to recreate the class
        #IN particular, we do NOT return the theLikeHAWC class,
        #which is not pickeable. It will instead be recreated
        #on the other side

        d = {}

        d['name']= self.name
        d['maptree'] = self.maptree
        d['response'] = self.response
        d['ntransits'] = self.ntransits
        d['model'] = self.model
        d['minChannel'] = self.minChannel
        d['maxChannel'] = self.maxChannel

        d['roi_ra'] = self.roi_ra

        if self.roi_ra is not None:

            d['roi_dec'] = self.roi_dec
            d['roi_radius'] = self.roi_radius

        return d

    def __setstate__( self, state ):

        #This is used by pickle to recreate the class on the remote
        #side
        name = state['name']
        maptree = state['maptree']
        response = state['response']
        ntransits = state['ntransits']

        #Now report the class to its state

        self.__init__( name, maptree, response, ntransits )

        if state['roi_ra'] is not None:

            self.setROI( state['roi_ra'], state['roi_dec'], state['roi_radius'], state['fixedROI'] )

        self.setActiveMeasurements( state['minChannel'], state['maxChannel'] )

        self.setModel( state['model'] )

    def setActiveMeasurements( self, minChannel, maxChannel ):

        self.minChannel = int( minChannel )
        self.maxChannel = int( maxChannel )

        if self.instanced:

            sys.stderr.write("Since the plugins was already used before, the change in active measurements" +
                             "will not be effective until you create a new JointLikelihood or Bayesian" +
                             "instance")

    def setModel( self, LikelihoodModelInstance ):
        '''
        Set the model to be used in the joint minimization. Must be a LikelihoodModel instance.
        '''

        #Instance the python - C++ bridge
        self.model = LikelihoodModelInstance

        self.pymodel = pyToCppModelInterface( self.model )

        #Now init the HAWC LIFF software

        try:

            #Load all sky
            #(ROI will be defined later)

            self.theLikeHAWC = liff_3ML.LikeHAWC(self.maptree,
                                                 self.ntransits,
                                                 self.response,
                                                 self.pymodel,
                                                 self.minChannel,
                                                 self.maxChannel,
                                                 self.fullsky)

            if self.roi_ra is None and self.fullsky:

                raise RuntimeError("You have to define a ROI with the setROI method")

            if self.roi_ra is not None and self.fullsky:

                self.theLikeHAWC.SetROI( self.roi_ra, self.roi_dec, self.roi_radius, self.fixedROI )

        except:

            print("Could not instance the LikeHAWC class from LIFF. " +
                               "Check that HAWC software is working")

            raise

        else:

            self.instanced = True

        #Now set a callback in the CommonNorm parameter, so that if the user or the fit
        #engine or the Bayesian sampler change the CommonNorm value, the change will be
        #propagated to the LikeHAWC instance

        self.nuisanceParameters['CommonNorm'].setCallback( self._CommonNormCallback )

    def _CommonNormCallback( self ):

        self.theLikeHAWC.SetCommonNorm( self.nuisanceParameters['CommonNorm'].value )

    def getName(self):
        '''
        Return a name for this dataset (likely set during the constructor)
        '''
        return self.name

    def activateCommonNorm( self ):

        self.fitCommonNorm = True

    def deactivateCommonNorm( self ):

        self.fitCommonNorm = False

    def getLogLike(self):

        '''
        Return the value of the log-likelihood with the current values for the
        parameters
        '''

        self.pymodel.update()

        logL = self.theLikeHAWC.getLogLike( self.fitCommonNorm )

        return logL

    def calcTS(self):

        '''
        Return the value of the log-likelihood test statistic, defined as
        2*[log(LL_model) - log(LL_bkg)]
        '''

        self.pymodel.update()

        TS = self.theLikeHAWC.calcTS( self.fitCommonNorm )

        return TS

    def getNuisanceParameters(self):
        '''
        Return a list of nuisance parameters. Return an empty list if there
        are no nuisance parameters
        '''

        return self.nuisanceParameters.keys()

    def innerFit(self):

        self.theLikeHAWC.SetBackgroundNormFree( self.fitCommonNorm )

        self.pymodel.update()

        logL = self.theLikeHAWC.getLogLike( self.fitCommonNorm )

        self.nuisanceParameters['CommonNorm'].setValue( self.theLikeHAWC.CommonNorm() )

        return logL

    def display( self, radius = 2.0 ):

        figs = []

        nsrc = self.model.getNumberOfPointSources()

        for srcid in range(nsrc):

            ra, dec = self.model.getPointSourcePosition( srcid )

            model = numpy.array( self.theLikeHAWC.GetTopHatExpectedExcesses( ra, dec, radius ) )

            signal = numpy.array(self.theLikeHAWC.GetTopHatExcesses( ra, dec, radius ) )

            bkg = numpy.array( self.theLikeHAWC.GetTopHatBackgrounds( ra, dec, radius ) )

            total = signal + bkg

            fig = plt.figure()

            gs = gridspec.GridSpec(2,1, height_ratios=[2,1])
            gs.update(hspace=0)

            sub = plt.subplot( gs[0] )

            nHitBins = numpy.arange( self.minChannel, self.maxChannel + 1 )

            sub.errorbar( nHitBins, total, yerr=numpy.sqrt(total),
                          capsize=0, color='black', label='Observation',
                          fmt='.')

            sub.plot( nHitBins, model + bkg, label='Model + bkg')

            plt.legend( bbox_to_anchor=(1.05, 1), loc=2, numpoints=1)

            #Residuals

            sub1 = plt.subplot( gs[1] )

            #Using model variance to account for low statistic

            resid = ( signal - model ) / model

            sub1.axhline( 0, linestyle='--' )

            sub1.errorbar( nHitBins, resid,
                           yerr=numpy.sqrt(total) / model,
                           capsize=0, fmt='.' )

            sub.set_xlim( [nHitBins.min() - 0.5, nHitBins.max() + 0.5] )

            sub.set_yscale("log", nonposy='clip')

            sub.set_ylabel("Counts per bin")

            #sub1.set_xscale("log")

            sub1.set_xlabel("Analysis bin")

            sub1.set_ylabel(r"$\frac{excess - mod.}{mod.}$",fontsize=20)

            sub1.set_xlim( [nHitBins.min() - 0.5, nHitBins.max() + 0.5] )

            sub.set_xticks( [] )
            sub1.set_xticks( nHitBins )

            figs.append(fig)

        return figs

    def writeModelMap(self, fileName):

        self.theLikeHAWC.WriteModelMap( fileName )

    def writeResidualMap(self, fileName):

        self.theLikeHAWC.WriteResidualMap( fileName )

<commit_msg>Flag to turn on Poisson fluctuation when making model map<commit_after>from threeML.plugin_prototype import PluginPrototype

from threeML.models.Parameter import Parameter

from threeML.io.fileUtils import fileExistingAndReadable, sanitizeFilename

from threeML.pyModelInterface import pyToCppModelInterface

from hawc import liff_3ML

import os, sys, collections

import matplotlib.pyplot as plt
from matplotlib import gridspec

import numpy

defaultMinChannel = 0
defaultMaxChannel = 9

__instrument_name = "HAWC"

class HAWCLike( PluginPrototype ):

    def __init__( self, name, maptree, response, ntransits, **kwargs ):

        #This controls if the likeHAWC class should load the entire
        #map or just a small disc around a source (faster).
        #Default is the latter, which is way faster. LIFF will decide
        #autonomously which ROI to use depending on the source model

        self.fullsky = False

        if 'fullsky' in kwargs.keys():

            self.fullsky = bool( kwargs['fullsky'] )

        self.name = str( name )

        #Sanitize files in input (expand variables and so on)

        self.maptree = os.path.abspath( sanitizeFilename( maptree ) )

        self.response = os.path.abspath( sanitizeFilename( response ) )

        #
        self.ntransits = ntransits

        #Check that they exists and can be read

        if not fileExistingAndReadable( self.maptree ):

            raise IOError("MapTree %s does not exist or is not readable" % maptree)

        if not fileExistingAndReadable( self.response ):

            raise IOError("Response %s does not exist or is not readable" % response)

        #Post-pone the creation of the LIFF instance to when
        #we have the likelihood model

        self.instanced = False

        #Default value for minChannel and maxChannel

        self.minChannel = int( defaultMinChannel )
        self.maxChannel = int( defaultMaxChannel )

        #By default the fit of the CommonNorm is deactivated

        self.deactivateCommonNorm()

        #This is to keep track of whether the user defined a ROI or not

        self.roi_ra = None

        #Further setup

        self.__setup()

    def setROI(self, ra, dec, radius, fixedROI=False):

        self.roi_ra = ra
        self.roi_dec = dec

        self.roi_radius = radius

        self.fixedROI = fixedROI

    def __setup(self):

        #I put this here so I can use it both from the __init__ both from
        #the __setstate__ methods

        #Create the dictionary of nuisance parameters

        self.nuisanceParameters = collections.OrderedDict()
        self.nuisanceParameters['CommonNorm'] = Parameter("CommonNorm",1.0,0.5,1.5,0.01,
                                                           fixed=True,nuisance=True)

    def __getstate__(self):

        #This method is used by pickle before attempting to pickle the class

        #Return only the objects needed to recreate the class
        #IN particular, we do NOT return the theLikeHAWC class,
        #which is not pickeable. It will instead be recreated
        #on the other side

        d = {}

        d['name']= self.name
        d['maptree'] = self.maptree
        d['response'] = self.response
        d['ntransits'] = self.ntransits
        d['model'] = self.model
        d['minChannel'] = self.minChannel
        d['maxChannel'] = self.maxChannel

        d['roi_ra'] = self.roi_ra

        if self.roi_ra is not None:

            d['roi_dec'] = self.roi_dec
            d['roi_radius'] = self.roi_radius

        return d

    def __setstate__( self, state ):

        #This is used by pickle to recreate the class on the remote
        #side
        name = state['name']
        maptree = state['maptree']
        response = state['response']
        ntransits = state['ntransits']

        #Now report the class to its state

        self.__init__( name, maptree, response, ntransits )

        if state['roi_ra'] is not None:

            self.setROI( state['roi_ra'], state['roi_dec'], state['roi_radius'], state['fixedROI'] )

        self.setActiveMeasurements( state['minChannel'], state['maxChannel'] )

        self.setModel( state['model'] )

    def setActiveMeasurements( self, minChannel, maxChannel ):

        self.minChannel = int( minChannel )
        self.maxChannel = int( maxChannel )

        if self.instanced:

            sys.stderr.write("Since the plugins was already used before, the change in active measurements" +
                             "will not be effective until you create a new JointLikelihood or Bayesian" +
                             "instance")

    def setModel( self, LikelihoodModelInstance ):
        '''
        Set the model to be used in the joint minimization. Must be a LikelihoodModel instance.
        '''

        #Instance the python - C++ bridge
        self.model = LikelihoodModelInstance

        self.pymodel = pyToCppModelInterface( self.model )

        #Now init the HAWC LIFF software

        try:

            #Load all sky
            #(ROI will be defined later)

            self.theLikeHAWC = liff_3ML.LikeHAWC(self.maptree,
                                                 self.ntransits,
                                                 self.response,
                                                 self.pymodel,
                                                 self.minChannel,
                                                 self.maxChannel,
                                                 self.fullsky)

            if self.roi_ra is None and self.fullsky:

                raise RuntimeError("You have to define a ROI with the setROI method")

            if self.roi_ra is not None and self.fullsky:

                self.theLikeHAWC.SetROI( self.roi_ra, self.roi_dec, self.roi_radius, self.fixedROI )

        except:

            print("Could not instance the LikeHAWC class from LIFF. " +
                               "Check that HAWC software is working")

            raise

        else:

            self.instanced = True

        #Now set a callback in the CommonNorm parameter, so that if the user or the fit
        #engine or the Bayesian sampler change the CommonNorm value, the change will be
        #propagated to the LikeHAWC instance

        self.nuisanceParameters['CommonNorm'].setCallback( self._CommonNormCallback )

    def _CommonNormCallback( self ):

        self.theLikeHAWC.SetCommonNorm( self.nuisanceParameters['CommonNorm'].value )

    def getName(self):
        '''
        Return a name for this dataset (likely set during the constructor)
        '''
        return self.name

    def activateCommonNorm( self ):

        self.fitCommonNorm = True

    def deactivateCommonNorm( self ):

        self.fitCommonNorm = False

    def getLogLike(self):

        '''
        Return the value of the log-likelihood with the current values for the
        parameters
        '''

        self.pymodel.update()

        logL = self.theLikeHAWC.getLogLike( self.fitCommonNorm )

        return logL

    def calcTS(self):

        '''
        Return the value of the log-likelihood test statistic, defined as
        2*[log(LL_model) - log(LL_bkg)]
        '''

        self.pymodel.update()

        TS = self.theLikeHAWC.calcTS( self.fitCommonNorm )

        return TS

    def getNuisanceParameters(self):
        '''
        Return a list of nuisance parameters. Return an empty list if there
        are no nuisance parameters
        '''

        return self.nuisanceParameters.keys()

    def innerFit(self):

        self.theLikeHAWC.SetBackgroundNormFree( self.fitCommonNorm )

        self.pymodel.update()

        logL = self.theLikeHAWC.getLogLike( self.fitCommonNorm )

        self.nuisanceParameters['CommonNorm'].setValue( self.theLikeHAWC.CommonNorm() )

        return logL

    def display( self, radius = 2.0 ):

        figs = []

        nsrc = self.model.getNumberOfPointSources()

        for srcid in range(nsrc):

            ra, dec = self.model.getPointSourcePosition( srcid )

            model = numpy.array( self.theLikeHAWC.GetTopHatExpectedExcesses( ra, dec, radius ) )

            signal = numpy.array(self.theLikeHAWC.GetTopHatExcesses( ra, dec, radius ) )

            bkg = numpy.array( self.theLikeHAWC.GetTopHatBackgrounds( ra, dec, radius ) )

            total = signal + bkg

            fig = plt.figure()

            gs = gridspec.GridSpec(2,1, height_ratios=[2,1])
            gs.update(hspace=0)

            sub = plt.subplot( gs[0] )

            nHitBins = numpy.arange( self.minChannel, self.maxChannel + 1 )

            sub.errorbar( nHitBins, total, yerr=numpy.sqrt(total),
                          capsize=0, color='black', label='Observation',
                          fmt='.')

            sub.plot( nHitBins, model + bkg, label='Model + bkg')

            plt.legend( bbox_to_anchor=(1.05, 1), loc=2, numpoints=1)

            #Residuals

            sub1 = plt.subplot( gs[1] )

            #Using model variance to account for low statistic

            resid = ( signal - model ) / model

            sub1.axhline( 0, linestyle='--' )

            sub1.errorbar( nHitBins, resid,
                           yerr=numpy.sqrt(total) / model,
                           capsize=0, fmt='.' )

            sub.set_xlim( [nHitBins.min() - 0.5, nHitBins.max() + 0.5] )

            sub.set_yscale("log", nonposy='clip')

            sub.set_ylabel("Counts per bin")

            #sub1.set_xscale("log")

            sub1.set_xlabel("Analysis bin")

            sub1.set_ylabel(r"$\frac{excess - mod.}{mod.}$",fontsize=20)

            sub1.set_xlim( [nHitBins.min() - 0.5, nHitBins.max() + 0.5] )

            sub.set_xticks( [] )
            sub1.set_xticks( nHitBins )

            figs.append(fig)

        return figs

    def writeModelMap(self, fileName, poisson=False):

        self.theLikeHAWC.WriteModelMap( fileName, poisson )

    def writeResidualMap(self, fileName):

        self.theLikeHAWC.WriteResidualMap( fileName )

<|endoftext|>


<commit_before># Prime Number Sieve
# https://www.nostarch.com/crackingcodes/ (BSD Licensed)

import math, random

def isPrimeTrialDiv(num):
    # Returns True if num is a prime number, otherwise False.

    # Uses the trial division algorithm for testing primality.

    # All numbers less than 2 are not prime:
    if num < 2:
        return False

    # See if num is divisible by any number up to the square root of num:
    for i in range(2, int(math.sqrt(num)) + 1):
        if num % i == 0:
            return False
    return True

def primeSieve(sieveSize):
    # Returns a list of prime numbers calculated using
    # the Sieve of Eratosthenes algorithm.

    sieve = [True] * sieveSize
    sieve[0] = False  # Zero and one are not prime numbers.
    sieve[1] = False

    # Create the sieve:
    for i in range(2, int(math.sqrt(sieveSize)) + 1):
        pointer = i * 2
        while pointer < sieveSize:
            sieve[pointer] = False
            pointer += i

    # Compile the list of primes:
    primes = []
    for i in range(sieveSize):
        if sieve[i] == True:
            primes.append(i)

    return primes

def rabinMiller(num):
    # Returns True if num is a prime number.
    if num % 2 == 0 or num < 2:
        return False  # Rabin-Miller doesn't work on even integers.
    if num == 3:
        return True
    s = num - 1
    t = 0
    while s % 2 == 0:
        # Keep halving s until it is odd (and use t
        # to count how many times we halve s):
        s = s // 2
        t += 1
    for trials in range(5):  # Try to falsify num's primality 5 times.
        a = random.randrange(2, num - 1)
        v = pow(a, s, num)
        if v != 1:  # This test does not apply if v is 1.
            i = 0
            while v != (num - 1):
                if i == t - 1:
                    return False
                else:
                    i = i + 1
                    v = (v ** 2) % num
    return True

# Most of the time we can quickly determine if num is not prime
# by dividing by the first few dozen prime numbers. This is quicker
# than rabinMiller() but does not detect all composites.
LOW_PRIMES = primeSieve(100)

def isPrime(num):
    # Return True if num is a prime numbers. This function does a quicker
    # prime number check before calling rabinMiller().
    if (num < 2):
        return False  # 0, 1, and negative numbers are not prime.

    # See if any of the low prime numbers can divide num:
    for prime in LOW_PRIMES:
        if (num % prime == 0):
            return False

    # If all else fails, call rabinMiller() to determine if num is prime:
    return rabinMiller(num)

def generateLargePrime(keysize=1024):
    # Return a random prime number that is keysize bits in size:
    while True:
        num = random.randrange(2**(keysize-1), 2**(keysize))
        if isPrime(num):
            return num
<commit_msg>Update primeNum: fixed PEP8 parenthesis<commit_after># Prime Number Sieve
# https://www.nostarch.com/crackingcodes/ (BSD Licensed)

import math, random

def isPrimeTrialDiv(num):
    # Returns True if num is a prime number, otherwise False.

    # Uses the trial division algorithm for testing primality.

    # All numbers less than 2 are not prime:
    if num < 2:
        return False

    # See if num is divisible by any number up to the square root of num:
    for i in range(2, int(math.sqrt(num)) + 1):
        if num % i == 0:
            return False
    return True

def primeSieve(sieveSize):
    # Returns a list of prime numbers calculated using
    # the Sieve of Eratosthenes algorithm.

    sieve = [True] * sieveSize
    sieve[0] = False  # Zero and one are not prime numbers.
    sieve[1] = False

    # Create the sieve:
    for i in range(2, int(math.sqrt(sieveSize)) + 1):
        pointer = i * 2
        while pointer < sieveSize:
            sieve[pointer] = False
            pointer += i

    # Compile the list of primes:
    primes = []
    for i in range(sieveSize):
        if sieve[i] == True:
            primes.append(i)

    return primes

def rabinMiller(num):
    # Returns True if num is a prime number.
    if num % 2 == 0 or num < 2:
        return False  # Rabin-Miller doesn't work on even integers.
    if num == 3:
        return True
    s = num - 1
    t = 0
    while s % 2 == 0:
        # Keep halving s until it is odd (and use t
        # to count how many times we halve s):
        s = s // 2
        t += 1
    for trials in range(5):  # Try to falsify num's primality 5 times.
        a = random.randrange(2, num - 1)
        v = pow(a, s, num)
        if v != 1:  # This test does not apply if v is 1.
            i = 0
            while v != (num - 1):
                if i == t - 1:
                    return False
                else:
                    i = i + 1
                    v = (v ** 2) % num
    return True

# Most of the time we can quickly determine if num is not prime
# by dividing by the first few dozen prime numbers. This is quicker
# than rabinMiller() but does not detect all composites.
LOW_PRIMES = primeSieve(100)

def isPrime(num):
    # Return True if num is a prime numbers. This function does a quicker
    # prime number check before calling rabinMiller().
    if num < 2:
        return False  # 0, 1, and negative numbers are not prime.

    # See if any of the low prime numbers can divide num:
    for prime in LOW_PRIMES:
        if num % prime == 0:
            return False

    # If all else fails, call rabinMiller() to determine if num is prime:
    return rabinMiller(num)

def generateLargePrime(keysize=1024):
    # Return a random prime number that is keysize bits in size:
    while True:
        num = random.randrange(2**(keysize-1), 2**keysize)
        if isPrime(num):
            return num
<|endoftext|>


<commit_before># Copyright 2019-present Kensho Technologies, LLC.
from collections import namedtuple
from typing import AbstractSet, Any, Dict, List, Mapping, Optional, Tuple, TypeVar, Union, cast

from graphql import (
    DocumentNode,
    EnumTypeDefinitionNode,
    FieldDefinitionNode,
    InterfaceTypeDefinitionNode,
    NamedTypeNode,
    Node,
    ObjectTypeDefinitionNode,
    UnionTypeDefinitionNode,
    build_ast_schema,
)
from graphql.language.visitor import Visitor, visit
import six

from .utils import (
    SchemaNameConflictError,
    check_ast_schema_is_valid,
    check_type_name_is_valid,
    get_copy_of_node_with_new_name,
    get_query_type_name,
    get_scalar_names,
)

RenamedSchemaDescriptor = namedtuple(
    "RenamedSchemaDescriptor",
    (
        "schema_ast",  # Document, AST representing the renamed schema
        "schema",  # GraphQLSchema, representing the same schema as schema_ast
        "reverse_name_map",  # Dict[str, str], renamed type/query type field name to original name
        # reverse_name_map only contains names that were changed
    ),
)

# Union of classes of nodes to be renamed by an instance of RenameSchemaTypesVisitor. Note that
# RenameSchemaTypesVisitor also has a class attribute rename_types which parallels the classes here.
# This duplication is necessary due to language and linter constraints-- see the comment in the
# RenameSchemaTypesVisitor class for more information.
# Unfortunately, RenameTypes itself has to be a module attribute instead of a class attribute
# because a bug in flake8 produces a linting error if RenameTypes is a class attribute and we type
# hint the return value of the RenameSchemaTypesVisitor's _rename_name_and_add_to_record() method as
# RenameTypes. More on this here: https://github.com/PyCQA/pyflakes/issues/441
RenameTypes = Union[
    EnumTypeDefinitionNode,
    InterfaceTypeDefinitionNode,
    NamedTypeNode,
    ObjectTypeDefinitionNode,
    UnionTypeDefinitionNode,
]
RenameTypesT = TypeVar("RenameTypesT", bound=RenameTypes)

def rename_schema(
    schema_ast: DocumentNode, renamings: Mapping[str, str]
) -> RenamedSchemaDescriptor:
    """Create a RenamedSchemaDescriptor; types and query type fields are renamed using renamings.

    Any type, interface, enum, or fields of the root type/query type whose name
    appears in renamings will be renamed to the corresponding value. Any such names that do not
    appear in renamings will be unchanged. Scalars, directives, enum values, and fields not
    belonging to the root/query type will never be renamed.

    Args:
        schema_ast: represents a valid schema that does not contain extensions, input object
                    definitions, mutations, or subscriptions, whose fields of the query type share
                    the same name as the types they query. Not modified by this function
        renamings: maps original type/field names to renamed type/field names. Type or query type
                   field names that do not appear in the dict will be unchanged. Any dict-like
                   object that implements get(key, [default]) may also be used

    Returns:
        RenamedSchemaDescriptor, a namedtuple that contains the AST of the renamed schema, and the
        map of renamed type/field names to original names. Only renamed names will be included
        in the map.

    Raises:
        - InvalidTypeNameError if the schema contains an invalid type name, or if the user attempts
          to rename a type to an invalid name. A name is considered invalid if it does not consist
          of alphanumeric characters and underscores, if it starts with a numeric character, or
          if it starts with double underscores
        - SchemaStructureError if the schema does not have the expected form; in particular, if
          the AST does not represent a valid schema, if any query type field does not have the
          same name as the type that it queries, if the schema contains type extensions or
          input object definitions, or if the schema contains mutations or subscriptions
        - SchemaNameConflictError if there are conflicts between the renamed types or fields
    """
    # Check input schema satisfies various structural requirements
    check_ast_schema_is_valid(schema_ast)

    schema = build_ast_schema(schema_ast)
    query_type = get_query_type_name(schema)
    scalars = get_scalar_names(schema)

    # Rename types, interfaces, enums
    schema_ast, reverse_name_map = _rename_types(schema_ast, renamings, query_type, scalars)
    reverse_name_map_changed_names_only = {
        renamed_name: original_name
        for renamed_name, original_name in six.iteritems(reverse_name_map)
        if renamed_name != original_name
    }

    # Rename query type fields
    schema_ast = _rename_query_type_fields(schema_ast, renamings, query_type)
    return RenamedSchemaDescriptor(
        schema_ast=schema_ast,
        schema=build_ast_schema(schema_ast),
        reverse_name_map=reverse_name_map_changed_names_only,
    )

def _rename_types(
    schema_ast: DocumentNode,
    renamings: Mapping[str, str],
    query_type: str,
    scalars: AbstractSet[str],
) -> Tuple[DocumentNode, Dict[str, str]]:
    """Rename types, enums, interfaces using renamings.

    The query type will not be renamed. Scalar types, field names, enum values will not be renamed.

    The input schema AST will not be modified.

    Args:
        schema_ast: schema that we're returning a modified version of
        renamings: maps original type/interface/enum name to renamed name. Any name not in the dict
                   will be unchanged
        query_type: name of the query type, e.g. 'RootSchemaQuery'
        scalars: set of all scalars used in the schema, including user defined scalars and used
                 builtin scalars, excluding unused builtins

    Returns:
        Tuple containing the modified version of the schema AST, and the renamed type name to
        original type name map. Map contains all types, including those that were not renamed.

    Raises:
        - InvalidTypeNameError if the schema contains an invalid type name, or if the user attempts
          to rename a type to an invalid name
        - SchemaNameConflictError if the rename causes name conflicts
    """
    visitor = RenameSchemaTypesVisitor(renamings, query_type, scalars)
    renamed_schema_ast = visit(schema_ast, visitor)
    return renamed_schema_ast, visitor.reverse_name_map

def _rename_query_type_fields(
    schema_ast: DocumentNode, renamings: Mapping[str, str], query_type: str
) -> DocumentNode:
    """Rename all fields of the query type.

    The input schema AST will not be modified.

    Args:
        schema_ast: schema that we're returning a modified version of
        renamings: maps original query type field name to renamed name. Any name not in the dict
                   will be unchanged
        query_type: name of the query type, e.g. 'RootSchemaQuery'

    Returns:
        modified version of the input schema AST
    """
    visitor = RenameQueryTypeFieldsVisitor(renamings, query_type)
    renamed_schema_ast = visit(schema_ast, visitor)
    return renamed_schema_ast

class RenameSchemaTypesVisitor(Visitor):
    """Traverse a Document AST, editing the names of nodes."""

    noop_types = frozenset(
        {
            "ArgumentNode",
            "BooleanValueNode",
            "DirectiveNode",
            "DirectiveDefinitionNode",
            "DocumentNode",
            "EnumValueNode",
            "EnumValueDefinitionNode",
            "FieldNode",
            "FieldDefinitionNode",
            "FloatValueNode",
            "FragmentDefinitionNode",
            "FragmentSpreadNode",
            "InlineFragmentNode",
            "InputObjectTypeDefinitionNode",
            "InputValueDefinitionNode",
            "IntValueNode",
            "ListTypeNode",
            "ListValueNode",
            "NameNode",
            "NonNullTypeNode",
            "ObjectFieldNode",
            "ObjectValueNode",
            "OperationDefinitionNode",
            "OperationTypeDefinitionNode",
            "ScalarTypeDefinitionNode",
            "SchemaDefinitionNode",
            "SelectionSetNode",
            "StringValueNode",
            "VariableNode",
            "VariableDefinitionNode",
            "SchemaExtensionNode",
            "InterfaceTypeExtensionNode",
            "UnionTypeExtensionNode",
            "EnumTypeExtensionNode",
            "ObjectTypeExtensionNode",
            "InputObjectTypeExtensionNode",
            "ScalarTypeExtensionNode",
        }
    )
    # rename_types must be a set of strings corresponding to the names of the classes in
    # RenameTypes. The duplication exists because introspection for Unions via typing.get_args()
    # doesn't exist until Python 3.8. In Python 3.8, this would be a valid way to define
    # rename_types:
    # rename_types = frozenset(cls.__name__ for cls in get_args(RenameTypes))  # type: ignore
    # Note: even with Python 3.8, the mypy version at the time of writing (version 0.770) doesn't
    # allow for introspection for Unions. mypy's maintainers recently merged a PR
    # (https://github.com/python/mypy/pull/8779) that permits this line of code, but did so after
    # the mypy 0.770 release. If we do end up removing the duplication at a later point but not
    # update the mypy version, we'd need to ignore it (as shown in the in-line comment).
    rename_types = frozenset(
        {
            "EnumTypeDefinitionNode",
            "InterfaceTypeDefinitionNode",
            "NamedTypeNode",
            "ObjectTypeDefinitionNode",
            "UnionTypeDefinitionNode",
        }
    )

    def __init__(
        self, renamings: Mapping[str, str], query_type: str, scalar_types: AbstractSet[str]
    ) -> None:
        """Create a visitor for renaming types in a schema AST.

        Args:
            renamings: maps original type name to renamed name. Any name not in the dict will be
                       unchanged
            query_type: name of the query type (e.g. RootSchemaQuery), which will not be renamed
            scalar_types: set of all scalars used in the schema, including all user defined scalars
                          and any builtin scalars that were used
        """
        self.renamings = renamings
        self.reverse_name_map: Dict[str, str] = {}  # from renamed type name to original type name
        # reverse_name_map contains all types, including those that were unchanged
        self.query_type = query_type
        self.scalar_types = frozenset(scalar_types)
        self.builtin_types = frozenset({"String", "Int", "Float", "Boolean", "ID"})

    def _rename_name_and_add_to_record(self, node: RenameTypesT) -> RenameTypesT:
        """Change the name of the input node if necessary, add the name pair to reverse_name_map.

        Don't rename if the type is the query type, a scalar type, or a builtin type.

        The input node will not be modified. reverse_name_map may be modified.

        Args:
            node: object representing an AST component, containing a .name attribute
                  corresponding to an AST node of type NameNode.

        Returns:
            Node object, identical to the input node, except with possibly a new name. If the
            name was not changed, the returned object is the exact same object as the input

        Raises:
            - InvalidTypeNameError if either the node's current name or renamed name is invalid
            - SchemaNameConflictError if the newly renamed node causes name conflicts with
              existing types, scalars, or builtin types
        """
        name_string = node.name.value

        if name_string == self.query_type or name_string in self.scalar_types:
            return node

        new_name_string = self.renamings.get(name_string, name_string)  # Default use original
        check_type_name_is_valid(new_name_string)

        if (
            new_name_string in self.reverse_name_map
            and self.reverse_name_map[new_name_string] != name_string
        ):
            raise SchemaNameConflictError(
                '"{}" and "{}" are both renamed to "{}"'.format(
                    name_string, self.reverse_name_map[new_name_string], new_name_string
                )
            )
        if new_name_string in self.scalar_types or new_name_string in self.builtin_types:
            raise SchemaNameConflictError(
                '"{}" was renamed to "{}", clashing with scalar "{}"'.format(
                    name_string, new_name_string, new_name_string
                )
            )

        self.reverse_name_map[new_name_string] = name_string
        if new_name_string == name_string:
            return node
        else:  # Make copy of node with the changed name, return the copy
            node_with_new_name = get_copy_of_node_with_new_name(node, new_name_string)
            return node_with_new_name

    def enter(
        self, node: Node, key: Any, parent: Any, path: List[Any], ancestors: List[Any],
    ) -> Optional[Node]:
        """Upon entering a node, operate depending on node type."""
        node_type = type(node).__name__
        if node_type in self.noop_types:
            # Do nothing, continue traversal
            return None
        elif node_type in self.rename_types:
            # Rename node, put name pair into record
            renamed_node = self._rename_name_and_add_to_record(cast(RenameTypes, node))
            if renamed_node is node:  # Name unchanged, continue traversal
                return None
            else:  # Name changed, return new node, `visit` will make shallow copies along path
                return renamed_node
        else:
            # All Node types should've been taken care of, this line should never be reached
            raise AssertionError('Unreachable code reached. Missed type: "{}"'.format(node_type))

class RenameQueryTypeFieldsVisitor(Visitor):
    def __init__(self, renamings: Mapping[str, str], query_type: str) -> None:
        """Create a visitor for renaming fields of the query type in a schema AST.

        Args:
            renamings: maps original field name to renamed field name. Any name not in the dict will
                       be unchanged
            query_type: name of the query type (e.g. RootSchemaQuery)
        """
        # Note that as field names and type names have been confirmed to match up, any renamed
        # field already has a corresponding renamed type. If no errors, due to either invalid
        # names or name conflicts, were raised when renaming type, no errors will occur when
        # renaming query type fields.
        self.in_query_type = False
        self.renamings = renamings
        self.query_type = query_type

    def enter_object_type_definition(
        self,
        node: ObjectTypeDefinitionNode,
        key: Any,
        parent: Any,
        path: List[Any],
        ancestors: List[Any],
    ) -> None:
        """If the node's name matches the query type, record that we entered the query type."""
        if node.name.value == self.query_type:
            self.in_query_type = True

    def leave_object_type_definition(
        self,
        node: ObjectTypeDefinitionNode,
        key: Any,
        parent: Any,
        path: List[Any],
        ancestors: List[Any],
    ) -> None:
        """If the node's name matches the query type, record that we left the query type."""
        if node.name.value == self.query_type:
            self.in_query_type = False

    def enter_field_definition(
        self,
        node: FieldDefinitionNode,
        key: Any,
        parent: Any,
        path: List[Any],
        ancestors: List[Any],
    ) -> Optional[Node]:
        """If inside the query type, rename field and add the name pair to reverse_field_map."""
        if self.in_query_type:
            field_name = node.name.value
            new_field_name = self.renamings.get(field_name, field_name)  # Default use original
            if new_field_name == field_name:
                return None
            else:  # Make copy of node with the changed name, return the copy
                field_node_with_new_name = get_copy_of_node_with_new_name(node, new_field_name)
                return field_node_with_new_name

        return None
<commit_msg>Switch return value from None to IDLE (#855)<commit_after># Copyright 2019-present Kensho Technologies, LLC.
from collections import namedtuple
from typing import AbstractSet, Any, Dict, List, Mapping, Tuple, TypeVar, Union, cast

from graphql import (
    DocumentNode,
    EnumTypeDefinitionNode,
    FieldDefinitionNode,
    InterfaceTypeDefinitionNode,
    NamedTypeNode,
    Node,
    ObjectTypeDefinitionNode,
    UnionTypeDefinitionNode,
    build_ast_schema,
)
from graphql.language.visitor import IDLE, Visitor, visit
import six

from .utils import (
    SchemaNameConflictError,
    check_ast_schema_is_valid,
    check_type_name_is_valid,
    get_copy_of_node_with_new_name,
    get_query_type_name,
    get_scalar_names,
)

RenamedSchemaDescriptor = namedtuple(
    "RenamedSchemaDescriptor",
    (
        "schema_ast",  # Document, AST representing the renamed schema
        "schema",  # GraphQLSchema, representing the same schema as schema_ast
        "reverse_name_map",  # Dict[str, str], renamed type/query type field name to original name
        # reverse_name_map only contains names that were changed
    ),
)

# Union of classes of nodes to be renamed by an instance of RenameSchemaTypesVisitor. Note that
# RenameSchemaTypesVisitor also has a class attribute rename_types which parallels the classes here.
# This duplication is necessary due to language and linter constraints-- see the comment in the
# RenameSchemaTypesVisitor class for more information.
# Unfortunately, RenameTypes itself has to be a module attribute instead of a class attribute
# because a bug in flake8 produces a linting error if RenameTypes is a class attribute and we type
# hint the return value of the RenameSchemaTypesVisitor's _rename_name_and_add_to_record() method as
# RenameTypes. More on this here: https://github.com/PyCQA/pyflakes/issues/441
RenameTypes = Union[
    EnumTypeDefinitionNode,
    InterfaceTypeDefinitionNode,
    NamedTypeNode,
    ObjectTypeDefinitionNode,
    UnionTypeDefinitionNode,
]
RenameTypesT = TypeVar("RenameTypesT", bound=RenameTypes)
# AST visitor functions can return a number of different things, such as returning a Node (to update
# that node) or returning a special value defined in graphql.visitor such as REMOVE (to remove the
# node) and IDLE (to do nothing with the node). In the current GraphQL-core version (>=3,<3.1),
# REMOVE is set to the singleton object Ellipsis and IDLE is set to None. However, because these
# special values' underlying definitions can change, we can't type-hint functions returning a
# special value with anything more specific than Any. For more information, see:
# https://github.com/kensho-technologies/graphql-compiler/pull/834#discussion_r434622400
# We can update this type hint when a future GraphQL-core release organizes these special return
# values into an enum.
# https://github.com/graphql-python/graphql-core/issues/96
VisitorReturnType = Union[Node, Any]

def rename_schema(
    schema_ast: DocumentNode, renamings: Mapping[str, str]
) -> RenamedSchemaDescriptor:
    """Create a RenamedSchemaDescriptor; types and query type fields are renamed using renamings.

    Any type, interface, enum, or fields of the root type/query type whose name
    appears in renamings will be renamed to the corresponding value. Any such names that do not
    appear in renamings will be unchanged. Scalars, directives, enum values, and fields not
    belonging to the root/query type will never be renamed.

    Args:
        schema_ast: represents a valid schema that does not contain extensions, input object
                    definitions, mutations, or subscriptions, whose fields of the query type share
                    the same name as the types they query. Not modified by this function
        renamings: maps original type/field names to renamed type/field names. Type or query type
                   field names that do not appear in the dict will be unchanged. Any dict-like
                   object that implements get(key, [default]) may also be used

    Returns:
        RenamedSchemaDescriptor, a namedtuple that contains the AST of the renamed schema, and the
        map of renamed type/field names to original names. Only renamed names will be included
        in the map.

    Raises:
        - InvalidTypeNameError if the schema contains an invalid type name, or if the user attempts
          to rename a type to an invalid name. A name is considered invalid if it does not consist
          of alphanumeric characters and underscores, if it starts with a numeric character, or
          if it starts with double underscores
        - SchemaStructureError if the schema does not have the expected form; in particular, if
          the AST does not represent a valid schema, if any query type field does not have the
          same name as the type that it queries, if the schema contains type extensions or
          input object definitions, or if the schema contains mutations or subscriptions
        - SchemaNameConflictError if there are conflicts between the renamed types or fields
    """
    # Check input schema satisfies various structural requirements
    check_ast_schema_is_valid(schema_ast)

    schema = build_ast_schema(schema_ast)
    query_type = get_query_type_name(schema)
    scalars = get_scalar_names(schema)

    # Rename types, interfaces, enums
    schema_ast, reverse_name_map = _rename_types(schema_ast, renamings, query_type, scalars)
    reverse_name_map_changed_names_only = {
        renamed_name: original_name
        for renamed_name, original_name in six.iteritems(reverse_name_map)
        if renamed_name != original_name
    }

    # Rename query type fields
    schema_ast = _rename_query_type_fields(schema_ast, renamings, query_type)
    return RenamedSchemaDescriptor(
        schema_ast=schema_ast,
        schema=build_ast_schema(schema_ast),
        reverse_name_map=reverse_name_map_changed_names_only,
    )

def _rename_types(
    schema_ast: DocumentNode,
    renamings: Mapping[str, str],
    query_type: str,
    scalars: AbstractSet[str],
) -> Tuple[DocumentNode, Dict[str, str]]:
    """Rename types, enums, interfaces using renamings.

    The query type will not be renamed. Scalar types, field names, enum values will not be renamed.

    The input schema AST will not be modified.

    Args:
        schema_ast: schema that we're returning a modified version of
        renamings: maps original type/interface/enum name to renamed name. Any name not in the dict
                   will be unchanged
        query_type: name of the query type, e.g. 'RootSchemaQuery'
        scalars: set of all scalars used in the schema, including user defined scalars and used
                 builtin scalars, excluding unused builtins

    Returns:
        Tuple containing the modified version of the schema AST, and the renamed type name to
        original type name map. Map contains all types, including those that were not renamed.

    Raises:
        - InvalidTypeNameError if the schema contains an invalid type name, or if the user attempts
          to rename a type to an invalid name
        - SchemaNameConflictError if the rename causes name conflicts
    """
    visitor = RenameSchemaTypesVisitor(renamings, query_type, scalars)
    renamed_schema_ast = visit(schema_ast, visitor)
    return renamed_schema_ast, visitor.reverse_name_map

def _rename_query_type_fields(
    schema_ast: DocumentNode, renamings: Mapping[str, str], query_type: str
) -> DocumentNode:
    """Rename all fields of the query type.

    The input schema AST will not be modified.

    Args:
        schema_ast: schema that we're returning a modified version of
        renamings: maps original query type field name to renamed name. Any name not in the dict
                   will be unchanged
        query_type: name of the query type, e.g. 'RootSchemaQuery'

    Returns:
        modified version of the input schema AST
    """
    visitor = RenameQueryTypeFieldsVisitor(renamings, query_type)
    renamed_schema_ast = visit(schema_ast, visitor)
    return renamed_schema_ast

class RenameSchemaTypesVisitor(Visitor):
    """Traverse a Document AST, editing the names of nodes."""

    noop_types = frozenset(
        {
            "ArgumentNode",
            "BooleanValueNode",
            "DirectiveNode",
            "DirectiveDefinitionNode",
            "DocumentNode",
            "EnumValueNode",
            "EnumValueDefinitionNode",
            "FieldNode",
            "FieldDefinitionNode",
            "FloatValueNode",
            "FragmentDefinitionNode",
            "FragmentSpreadNode",
            "InlineFragmentNode",
            "InputObjectTypeDefinitionNode",
            "InputValueDefinitionNode",
            "IntValueNode",
            "ListTypeNode",
            "ListValueNode",
            "NameNode",
            "NonNullTypeNode",
            "ObjectFieldNode",
            "ObjectValueNode",
            "OperationDefinitionNode",
            "OperationTypeDefinitionNode",
            "ScalarTypeDefinitionNode",
            "SchemaDefinitionNode",
            "SelectionSetNode",
            "StringValueNode",
            "VariableNode",
            "VariableDefinitionNode",
            "SchemaExtensionNode",
            "InterfaceTypeExtensionNode",
            "UnionTypeExtensionNode",
            "EnumTypeExtensionNode",
            "ObjectTypeExtensionNode",
            "InputObjectTypeExtensionNode",
            "ScalarTypeExtensionNode",
        }
    )
    # rename_types must be a set of strings corresponding to the names of the classes in
    # RenameTypes. The duplication exists because introspection for Unions via typing.get_args()
    # doesn't exist until Python 3.8. In Python 3.8, this would be a valid way to define
    # rename_types:
    # rename_types = frozenset(cls.__name__ for cls in get_args(RenameTypes))  # type: ignore
    # Note: even with Python 3.8, the mypy version at the time of writing (version 0.770) doesn't
    # allow for introspection for Unions. mypy's maintainers recently merged a PR
    # (https://github.com/python/mypy/pull/8779) that permits this line of code, but did so after
    # the mypy 0.770 release. If we do end up removing the duplication at a later point but not
    # update the mypy version, we'd need to ignore it (as shown in the in-line comment).
    rename_types = frozenset(
        {
            "EnumTypeDefinitionNode",
            "InterfaceTypeDefinitionNode",
            "NamedTypeNode",
            "ObjectTypeDefinitionNode",
            "UnionTypeDefinitionNode",
        }
    )

    def __init__(
        self, renamings: Mapping[str, str], query_type: str, scalar_types: AbstractSet[str]
    ) -> None:
        """Create a visitor for renaming types in a schema AST.

        Args:
            renamings: maps original type name to renamed name. Any name not in the dict will be
                       unchanged
            query_type: name of the query type (e.g. RootSchemaQuery), which will not be renamed
            scalar_types: set of all scalars used in the schema, including all user defined scalars
                          and any builtin scalars that were used
        """
        self.renamings = renamings
        self.reverse_name_map: Dict[str, str] = {}  # from renamed type name to original type name
        # reverse_name_map contains all types, including those that were unchanged
        self.query_type = query_type
        self.scalar_types = frozenset(scalar_types)
        self.builtin_types = frozenset({"String", "Int", "Float", "Boolean", "ID"})

    def _rename_name_and_add_to_record(self, node: RenameTypesT) -> RenameTypesT:
        """Change the name of the input node if necessary, add the name pair to reverse_name_map.

        Don't rename if the type is the query type, a scalar type, or a builtin type.

        The input node will not be modified. reverse_name_map may be modified.

        Args:
            node: object representing an AST component, containing a .name attribute
                  corresponding to an AST node of type NameNode.

        Returns:
            Node object, identical to the input node, except with possibly a new name. If the
            name was not changed, the returned object is the exact same object as the input

        Raises:
            - InvalidTypeNameError if either the node's current name or renamed name is invalid
            - SchemaNameConflictError if the newly renamed node causes name conflicts with
              existing types, scalars, or builtin types
        """
        name_string = node.name.value

        if name_string == self.query_type or name_string in self.scalar_types:
            return node

        new_name_string = self.renamings.get(name_string, name_string)  # Default use original
        check_type_name_is_valid(new_name_string)

        if (
            new_name_string in self.reverse_name_map
            and self.reverse_name_map[new_name_string] != name_string
        ):
            raise SchemaNameConflictError(
                '"{}" and "{}" are both renamed to "{}"'.format(
                    name_string, self.reverse_name_map[new_name_string], new_name_string
                )
            )
        if new_name_string in self.scalar_types or new_name_string in self.builtin_types:
            raise SchemaNameConflictError(
                '"{}" was renamed to "{}", clashing with scalar "{}"'.format(
                    name_string, new_name_string, new_name_string
                )
            )

        self.reverse_name_map[new_name_string] = name_string
        if new_name_string == name_string:
            return node
        else:  # Make copy of node with the changed name, return the copy
            node_with_new_name = get_copy_of_node_with_new_name(node, new_name_string)
            return node_with_new_name

    def enter(
        self, node: Node, key: Any, parent: Any, path: List[Any], ancestors: List[Any],
    ) -> VisitorReturnType:
        """Upon entering a node, operate depending on node type."""
        node_type = type(node).__name__
        if node_type in self.noop_types:
            # Do nothing, continue traversal
            return IDLE
        elif node_type in self.rename_types:
            # Rename node, put name pair into record
            renamed_node = self._rename_name_and_add_to_record(cast(RenameTypes, node))
            if renamed_node is node:  # Name unchanged, continue traversal
                return IDLE
            else:  # Name changed, return new node, `visit` will make shallow copies along path
                return renamed_node
        else:
            # All Node types should've been taken care of, this line should never be reached
            raise AssertionError('Unreachable code reached. Missed type: "{}"'.format(node_type))

class RenameQueryTypeFieldsVisitor(Visitor):
    def __init__(self, renamings: Mapping[str, str], query_type: str) -> None:
        """Create a visitor for renaming fields of the query type in a schema AST.

        Args:
            renamings: maps original field name to renamed field name. Any name not in the dict will
                       be unchanged
            query_type: name of the query type (e.g. RootSchemaQuery)
        """
        # Note that as field names and type names have been confirmed to match up, any renamed
        # field already has a corresponding renamed type. If no errors, due to either invalid
        # names or name conflicts, were raised when renaming type, no errors will occur when
        # renaming query type fields.
        self.in_query_type = False
        self.renamings = renamings
        self.query_type = query_type

    def enter_object_type_definition(
        self,
        node: ObjectTypeDefinitionNode,
        key: Any,
        parent: Any,
        path: List[Any],
        ancestors: List[Any],
    ) -> None:
        """If the node's name matches the query type, record that we entered the query type."""
        if node.name.value == self.query_type:
            self.in_query_type = True

    def leave_object_type_definition(
        self,
        node: ObjectTypeDefinitionNode,
        key: Any,
        parent: Any,
        path: List[Any],
        ancestors: List[Any],
    ) -> None:
        """If the node's name matches the query type, record that we left the query type."""
        if node.name.value == self.query_type:
            self.in_query_type = False

    def enter_field_definition(
        self,
        node: FieldDefinitionNode,
        key: Any,
        parent: Any,
        path: List[Any],
        ancestors: List[Any],
    ) -> VisitorReturnType:
        """If inside the query type, rename field and add the name pair to reverse_field_map."""
        if self.in_query_type:
            field_name = node.name.value
            new_field_name = self.renamings.get(field_name, field_name)  # Default use original
            if new_field_name == field_name:
                return IDLE
            else:  # Make copy of node with the changed name, return the copy
                field_node_with_new_name = get_copy_of_node_with_new_name(node, new_field_name)
                return field_node_with_new_name

        return IDLE
<|endoftext|>


<commit_before># Copyright 2017 The TensorFlow Authors. All Rights Reserved.
#
# 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.
# ==============================================================================

"""DelayCompensatedGradientDescentOptimizer for TensorFlow."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from tensorflow.python.framework import ops
from tensorflow.python.ops import state_ops
from tensorflow.python.ops import math_ops
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import variables
from tensorflow.python.training import optimizer
from tensorflow.python.training import training_ops

class _RefVariableAsynchronousProcessor(optimizer._RefVariableProcessor):
  """Processor for Variable."""
  def update_op_asynchronous(self, optimizer, g, index):
    if isinstance(g, ops.Tensor):
      return optimizer._apply_dense(g, self._v, index)
    else:
      assert isinstance(g, ops.IndexedSlices), ("Gradient ", g, " is neither a "
                                                "tensor nor IndexedSlices.")
      # pylint: disable=protected-access
      return optimizer._apply_sparse_duplicate_indices(g, self._v, index)

class _DenseResourceVariableAsynchronousProcessor(optimizer._DenseResourceVariableProcessor):
  """Processor for dense ResourceVariables."""
  def update_op_asynchronous(self, optimizer, g, index):
    # pylint: disable=protected-access
    if isinstance(g, ops.IndexedSlices):
      return optimizer._resource_apply_sparse_duplicate_indices(
        g.values, self._v, g.indices, index)
    return optimizer._resource_apply_dense(g, self._v, index)

def _get_processor(v):
  """The processor of v."""
  if isinstance(v, variables.Variable):
    return _RefVariableAsynchronousProcessor(v)
  if v.op.type == "VarHandleOp":
    return _DenseResourceVariableAsynchronousProcessor(v)
  raise NotImplementedError("Trying to optimize unsupported type ", v)

class DelayCompensatedGradientDescentOptimizer(optimizer.Optimizer):
  """Optimizer that implements the gradient descent algorithm with delay
  compensation.

  See [Zheng, Shuxin, et al., 2016](https://arxiv.org/abs/1609.08326)
  ([pdf](https://arxiv.org/pdf/1609.08326.pdf)).
  """

  def __init__(self, learning_rate, variance_parameter, num_workers=1,
               use_locking=False, name="DelayCompensatedGradientDescent"):
    """Construct a new gradient descent optimizer with delay compensation.

    Args:
      learning_rate: A Tensor or a floating point value.  The learning
        rate to use.
      variance_parameter: A Tensor or a floating point value. The lambda
        value to use.
      num_workers: A value to indicate number of workers computing gradients
        asynchronously.
      use_locking: If True use locks for update operations.
      name: Optional name prefix for the operations created when applying
        gradients. Defaults to "DelayCompensatedGradientDescent".
      """
    if num_workers <= 0:
      raise ValueError("num_workers must be positive: %s" % num_workers)
    super(DelayCompensatedGradientDescentOptimizer, self).__init__(
          use_locking, name)
    self._learning_rate = learning_rate
    self._lambda = variance_parameter
    self._num_workers = num_workers

  def minimize(self, loss, global_step=None, var_list=None,
               gate_gradients=optimizer.Optimizer.GATE_OP, aggregation_method=None,
               colocate_gradients_with_ops=False, name=None,
               grad_loss=None, worker_index=None):
    """Add operations to minimize `loss` by updating `var_list`.

    This method simply combines calls `compute_gradients()` and
    `apply_gradients()`. If you want to process the gradient before applying
    them call `compute_gradients()` and `apply_gradients()` explicitly instead
    of using this function.

    Args:
      loss: A `Tensor` containing the value to minimize.
      global_step: Optional `Variable` to increment by one after the
        variables have been updated.
      var_list: Optional list or tuple of `Variable` objects to update to
        minimize `loss`.  Defaults to the list of variables collected in
        the graph under the key `GraphKeys.TRAINABLE_VARIABLES`.
      gate_gradients: How to gate the computation of gradients.  Can be
        `GATE_NONE`, `GATE_OP`, or  `GATE_GRAPH`.
      aggregation_method: Specifies the method used to combine gradient terms.
        Valid values are defined in the class `AggregationMethod`.
      colocate_gradients_with_ops: If True, try colocating gradients with
        the corresponding op.
      name: Optional name for the returned operation.
      grad_loss: Optional. A `Tensor` holding the gradient computed for `loss`.
      worker_index: Optional. A value to indicate the instance of worker
        minimizing if computing asynchronously.

    Returns:
      An Operation that updates the variables in `var_list`.  If `global_step`
      was not `None`, that operation also increments `global_step`.

    Raises:
      ValueError: If some of the variables are not `Variable` objects.
    """
    if (worker_index < 0 and worker_index is not None) or worker_index >= self._num_workers:
      raise ValueError("worker index must be in the range [0, num_workers): %s" %
                        worker_index)
    grads_and_vars = self.compute_gradients(
        loss, var_list=var_list, gate_gradients=gate_gradients,
        aggregation_method=aggregation_method,
        colocate_gradients_with_ops=colocate_gradients_with_ops,
        grad_loss=grad_loss)

    vars_with_grad = [v for g, v in grads_and_vars if g is not None]
    if not vars_with_grad:
      raise ValueError(
          "No gradients provided for any variable, check your graph for ops"
          " that do not support gradients, between variables %s and loss %s." %
          ([str(v) for _, v in grads_and_vars], loss))

    return self.apply_gradients(grads_and_vars, global_step=global_step,
                                name=name, worker_index=worker_index)

  def apply_gradients(self,
                      grads_and_vars,
                      global_step=None,
                      name=None,
                      worker_index=None):
    """Apply gradients to variables.

    This is the second part of `minimize()`. It returns an `Operation` that
    applies gradients.

    Args:
      grads_and_vars: List of (gradient, variable) pairs as returned by
        `compute_gradients()`.
      global_step: Optional `Variable` to increment by one after the
        variables have been updated.
      name: Optional name for the returned operation.  Default to the
        name passed to the `Optimizer` constructor.
      worker_index: Optional value to indicate the instance of worker
        minimizing if computing asynchronously.

    Returns:
      An `Operation` that applies the specified gradients. If `global_step`
      was not None, that operation also increments `global_step`.

    Raises:
      TypeError: If `grads_and_vars` is malformed.
      ValueError: If none of the variables have gradients.
    """
    # This is a default implementation of apply_gradients() that can be shared
    # by most optimizers.  It relies on the subclass implementing the following
    # methods: _create_slots(), _prepare(), _apply_dense(), and _apply_sparse().

    grads_and_vars = tuple(grads_and_vars)  # Make sure repeat iteration works.
    if not grads_and_vars:
      raise ValueError("No variables provided.")
    converted_grads_and_vars = []
    for g, v in grads_and_vars:
      if g is not None:
        try:
          # Convert the grad to Tensor or IndexedSlices if necessary.
          g = ops.convert_to_tensor_or_indexed_slices(g)
        except TypeError:
          raise TypeError(
              "Gradient must be convertible to a Tensor"
              " or IndexedSlices, or None: %s" % g)
        if not isinstance(g, (ops.Tensor, ops.IndexedSlices)):
          raise TypeError(
              "Gradient must be a Tensor, IndexedSlices, or None: %s" % g)
      p = _get_processor(v)
      converted_grads_and_vars.append((g, v, p))

    converted_grads_and_vars = tuple(converted_grads_and_vars)
    var_list = [v for g, v, _ in converted_grads_and_vars if g is not None]
    if not var_list:
      raise ValueError("No gradients provided for any variable: %s." %
                       ([str(v) for _, _, v in converted_grads_and_vars],))
    with ops.control_dependencies(None):
      self._create_slots([optimizer._get_variable_for(v) for v in var_list])
    update_ops = []
    with ops.name_scope(name, self._name) as name:
      self._prepare()
      for grad, var, processor in converted_grads_and_vars:
        if grad is None:
          continue
        # We colocate all ops created in _apply_dense or _apply_sparse
        # on the same device as the variable.
        with ops.name_scope("update_" + var.op.name), ops.colocate_with(var):
          if worker_index is None:
            update_ops.append(processor.update_op(self, grad))
          else:
            update_ops.append(processor.update_op_asynchronous(self, grad,
                                                               worker_index))
      if global_step is None:
        apply_updates = self._finish(update_ops, name)
      else:
        with ops.control_dependencies([self._finish(update_ops, "update")]):
          with ops.colocate_with(global_step):
            apply_updates = state_ops.assign_add(global_step, 1, name=name).op

      train_op = ops.get_collection_ref(ops.GraphKeys.TRAIN_OP)
      if apply_updates not in train_op:
        train_op.append(apply_updates)

      return apply_updates

  def _create_slots(self, var_list):
    """Initialize slots for all the vars of each worker to store
        the previous values of it
    """
    for index in range(self._num_workers):
      for v in var_list:
        var2 = array_ops.identity(v.initialized_value())
        self._get_or_make_slot(v, var2, "shadow_{0}".format(index),
                               self._name)

  def _apply_dense(self, grad, var, worker_index=0):
    # Get previous value of the variable from the slot
    shadow = self.get_slot(var, "shadow_{0}".format(worker_index))
    return training_ops.apply_delay_compensated_gradient_descent(
        var.handle,
        math_ops.cast(self._learning_rate_tensor, grad.dtype.base_dtype),
        grad,
        math_ops.cast(self._lambda_tensor, grad.dtype.base_dtype),
        shadow.handle,
        use_locking=self._use_locking)

  def _prepare(self):
    self._learning_rate_tensor = ops.convert_to_tensor(self._learning_rate,
                                                       name="learning_rate")
    self._lambda_tensor = ops.convert_to_tensor(self._lambda,
                                                name="lambda")
<commit_msg>Change method to resource<commit_after># Copyright 2017 The TensorFlow Authors. All Rights Reserved.
#
# 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.
# ==============================================================================

"""DelayCompensatedGradientDescentOptimizer for TensorFlow."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from tensorflow.python.framework import ops
from tensorflow.python.ops import state_ops
from tensorflow.python.ops import math_ops
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import variables
from tensorflow.python.training import optimizer
from tensorflow.python.training import training_ops

class _RefVariableAsynchronousProcessor(optimizer._RefVariableProcessor):
  """Processor for Variable."""
  def update_op_asynchronous(self, optimizer, g, index):
    if isinstance(g, ops.Tensor):
      return optimizer._apply_dense(g, self._v, index)
    else:
      assert isinstance(g, ops.IndexedSlices), ("Gradient ", g, " is neither a "
                                                "tensor nor IndexedSlices.")
      # pylint: disable=protected-access
      return optimizer._apply_sparse_duplicate_indices(g, self._v, index)

class _DenseResourceVariableAsynchronousProcessor(optimizer._DenseResourceVariableProcessor):
  """Processor for dense ResourceVariables."""
  def update_op_asynchronous(self, optimizer, g, index):
    # pylint: disable=protected-access
    if isinstance(g, ops.IndexedSlices):
      return optimizer._resource_apply_sparse_duplicate_indices(
        g.values, self._v, g.indices, index)
    return optimizer._resource_apply_dense(g, self._v, index)

def _get_processor(v):
  """The processor of v."""
  if v.op.type == "VarHandleOp":
    return _DenseResourceVariableAsynchronousProcessor(v)
  if isinstance(v, variables.Variable):
    return _RefVariableAsynchronousProcessor(v)
  raise NotImplementedError("Trying to optimize unsupported type ", v)

class DelayCompensatedGradientDescentOptimizer(optimizer.Optimizer):
  """Optimizer that implements the gradient descent algorithm with delay
  compensation.

  See [Zheng, Shuxin, et al., 2016](https://arxiv.org/abs/1609.08326)
  ([pdf](https://arxiv.org/pdf/1609.08326.pdf)).
  """

  def __init__(self, learning_rate, variance_parameter, num_workers=1,
               use_locking=False, name="DelayCompensatedGradientDescent"):
    """Construct a new gradient descent optimizer with delay compensation.

    Args:
      learning_rate: A Tensor or a floating point value.  The learning
        rate to use.
      variance_parameter: A Tensor or a floating point value. The lambda
        value to use.
      num_workers: A value to indicate number of workers computing gradients
        asynchronously.
      use_locking: If True use locks for update operations.
      name: Optional name prefix for the operations created when applying
        gradients. Defaults to "DelayCompensatedGradientDescent".
      """
    if num_workers <= 0:
      raise ValueError("num_workers must be positive: %s" % num_workers)
    super(DelayCompensatedGradientDescentOptimizer, self).__init__(
          use_locking, name)
    self._learning_rate = learning_rate
    self._lambda = variance_parameter
    self._num_workers = num_workers

  def minimize(self, loss, global_step=None, var_list=None,
               gate_gradients=optimizer.Optimizer.GATE_OP, aggregation_method=None,
               colocate_gradients_with_ops=False, name=None,
               grad_loss=None, worker_index=None):
    """Add operations to minimize `loss` by updating `var_list`.

    This method simply combines calls `compute_gradients()` and
    `apply_gradients()`. If you want to process the gradient before applying
    them call `compute_gradients()` and `apply_gradients()` explicitly instead
    of using this function.

    Args:
      loss: A `Tensor` containing the value to minimize.
      global_step: Optional `Variable` to increment by one after the
        variables have been updated.
      var_list: Optional list or tuple of `Variable` objects to update to
        minimize `loss`.  Defaults to the list of variables collected in
        the graph under the key `GraphKeys.TRAINABLE_VARIABLES`.
      gate_gradients: How to gate the computation of gradients.  Can be
        `GATE_NONE`, `GATE_OP`, or  `GATE_GRAPH`.
      aggregation_method: Specifies the method used to combine gradient terms.
        Valid values are defined in the class `AggregationMethod`.
      colocate_gradients_with_ops: If True, try colocating gradients with
        the corresponding op.
      name: Optional name for the returned operation.
      grad_loss: Optional. A `Tensor` holding the gradient computed for `loss`.
      worker_index: Optional. A value to indicate the instance of worker
        minimizing if computing asynchronously.

    Returns:
      An Operation that updates the variables in `var_list`.  If `global_step`
      was not `None`, that operation also increments `global_step`.

    Raises:
      ValueError: If some of the variables are not `Variable` objects.
    """
    if (worker_index < 0 and worker_index is not None) or worker_index >= self._num_workers:
      raise ValueError("worker index must be in the range [0, num_workers): %s" %
                        worker_index)
    grads_and_vars = self.compute_gradients(
        loss, var_list=var_list, gate_gradients=gate_gradients,
        aggregation_method=aggregation_method,
        colocate_gradients_with_ops=colocate_gradients_with_ops,
        grad_loss=grad_loss)

    vars_with_grad = [v for g, v in grads_and_vars if g is not None]
    if not vars_with_grad:
      raise ValueError(
          "No gradients provided for any variable, check your graph for ops"
          " that do not support gradients, between variables %s and loss %s." %
          ([str(v) for _, v in grads_and_vars], loss))

    return self.apply_gradients(grads_and_vars, global_step=global_step,
                                name=name, worker_index=worker_index)

  def apply_gradients(self,
                      grads_and_vars,
                      global_step=None,
                      name=None,
                      worker_index=None):
    """Apply gradients to variables.

    This is the second part of `minimize()`. It returns an `Operation` that
    applies gradients.

    Args:
      grads_and_vars: List of (gradient, variable) pairs as returned by
        `compute_gradients()`.
      global_step: Optional `Variable` to increment by one after the
        variables have been updated.
      name: Optional name for the returned operation.  Default to the
        name passed to the `Optimizer` constructor.
      worker_index: Optional value to indicate the instance of worker
        minimizing if computing asynchronously.

    Returns:
      An `Operation` that applies the specified gradients. If `global_step`
      was not None, that operation also increments `global_step`.

    Raises:
      TypeError: If `grads_and_vars` is malformed.
      ValueError: If none of the variables have gradients.
    """
    # This is a default implementation of apply_gradients() that can be shared
    # by most optimizers.  It relies on the subclass implementing the following
    # methods: _create_slots(), _prepare(), _apply_dense(), and _apply_sparse().

    grads_and_vars = tuple(grads_and_vars)  # Make sure repeat iteration works.
    if not grads_and_vars:
      raise ValueError("No variables provided.")
    converted_grads_and_vars = []
    for g, v in grads_and_vars:
      if g is not None:
        try:
          # Convert the grad to Tensor or IndexedSlices if necessary.
          g = ops.convert_to_tensor_or_indexed_slices(g)
        except TypeError:
          raise TypeError(
              "Gradient must be convertible to a Tensor"
              " or IndexedSlices, or None: %s" % g)
        if not isinstance(g, (ops.Tensor, ops.IndexedSlices)):
          raise TypeError(
              "Gradient must be a Tensor, IndexedSlices, or None: %s" % g)
      p = _get_processor(v)
      converted_grads_and_vars.append((g, v, p))

    converted_grads_and_vars = tuple(converted_grads_and_vars)
    var_list = [v for g, v, _ in converted_grads_and_vars if g is not None]
    if not var_list:
      raise ValueError("No gradients provided for any variable: %s." %
                       ([str(v) for _, _, v in converted_grads_and_vars],))
    with ops.control_dependencies(None):
      self._create_slots([optimizer._get_variable_for(v) for v in var_list])
    update_ops = []
    with ops.name_scope(name, self._name) as name:
      self._prepare()
      for grad, var, processor in converted_grads_and_vars:
        if grad is None:
          continue
        # We colocate all ops created in _apply_dense or _apply_sparse
        # on the same device as the variable.
        with ops.name_scope("update_" + var.op.name), ops.colocate_with(var):
          if worker_index is None:
            update_ops.append(processor.update_op(self, grad))
          else:
            update_ops.append(processor.update_op_asynchronous(self, grad,
                                                               worker_index))
      if global_step is None:
        apply_updates = self._finish(update_ops, name)
      else:
        with ops.control_dependencies([self._finish(update_ops, "update")]):
          with ops.colocate_with(global_step):
            apply_updates = state_ops.assign_add(global_step, 1, name=name).op

      train_op = ops.get_collection_ref(ops.GraphKeys.TRAIN_OP)
      if apply_updates not in train_op:
        train_op.append(apply_updates)

      return apply_updates

  def _create_slots(self, var_list):
    """Initialize slots for all the vars of each worker to store
        the previous values of it
    """
    for index in range(self._num_workers):
      for v in var_list:
        var2 = array_ops.identity(v.initialized_value())
        self._get_or_make_slot(v, var2, "shadow_{0}".format(index),
                               self._name)

  def _resource_apply_dense(self, grad, var, worker_index=0):
    # Get previous value of the variable from the slot
    shadow = self.get_slot(var, "shadow_{0}".format(worker_index))
    return training_ops.apply_delay_compensated_gradient_descent(
        var.handle,
        math_ops.cast(self._learning_rate_tensor, grad.dtype.base_dtype),
        grad,
        math_ops.cast(self._lambda_tensor, grad.dtype.base_dtype),
        shadow.handle,
        use_locking=self._use_locking)

  def _prepare(self):
    self._learning_rate_tensor = ops.convert_to_tensor(self._learning_rate,
                                                       name="learning_rate")
    self._lambda_tensor = ops.convert_to_tensor(self._lambda,
                                                name="lambda")
<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import argparse
import multiprocessing as mp
import neblio_ci_libs as nci

nci.setup_travis_or_gh_actions_env_vars()

working_dir = os.getcwd()
build_dir = "build"
deploy_dir = os.path.join(os.environ['BUILD_DIR'],'deploy', '')

parser = argparse.ArgumentParser()
parser.add_argument('--test', '-t', help='Only build and run tests', action='store_true')
args = parser.parse_args()

nci.mkdir_p(deploy_dir)
nci.mkdir_p(build_dir)
os.chdir(build_dir)

# do not auto update homebrew as it is very slow
os.environ['HOMEBREW_NO_AUTO_UPDATE'] = '1'

# set up ccache
nci.call_with_err_code('brew fetch --retry ccache        && brew install ccache --force')

nci.call_with_err_code('brew fetch --retry qt            && brew install qt --force')
nci.call_with_err_code('brew fetch --retry berkeley-db@4 && brew install berkeley-db@4 --force')
nci.call_with_err_code('brew fetch --retry boost@1.60    && brew install boost@1.60 --force')
nci.call_with_err_code('brew fetch --retry miniupnpc     && brew install miniupnpc --force')
nci.call_with_err_code('brew fetch --retry curl          && brew install curl --force')
nci.call_with_err_code('brew fetch --retry openssl       && brew install openssl --force')
nci.call_with_err_code('brew fetch --retry qrencode      && brew install qrencode --force')

nci.call_with_err_code('brew unlink qt            && brew link --force --overwrite qt')
nci.call_with_err_code('brew unlink berkeley-db@4 && brew link --force --overwrite berkeley-db@4')
nci.call_with_err_code('brew unlink boost@1.60    && brew link --force --overwrite boost@1.60')
nci.call_with_err_code('brew unlink miniupnpc     && brew link --force --overwrite miniupnpc')
nci.call_with_err_code('brew unlink curl          && brew link --force --overwrite curl')
nci.call_with_err_code('brew unlink python        && brew link --force --overwrite python')
nci.call_with_err_code('brew unlink openssl       && brew link --force --overwrite openssl')
nci.call_with_err_code('brew unlink qrencode      && brew link --force --overwrite qrencode')

nci.call_with_err_code('ccache -s')

# prepend ccache to the path, necessary since prior steps prepend things to the path
os.environ['PATH'] = '/usr/local/opt/ccache/libexec:' + os.environ['PATH']

if (args.test):
    nci.call_with_err_code('qmake "QMAKE_CXX=ccache clang++" "USE_UPNP=1" "USE_QRCODE=1" "RELEASE=1" "NEBLIO_CONFIG += NoWallet" ../neblio-wallet.pro')
    nci.call_with_err_code("make -j" + str(mp.cpu_count()))
    # run tests
    nci.call_with_err_code("./wallet/test/neblio-Qt.app/Contents/MacOS/neblio-Qt")
else:
    nci.call_with_err_code('qmake "QMAKE_CXX=ccache clang++" "USE_UPNP=1" "USE_QRCODE=1" "RELEASE=1" ../neblio-wallet.pro')
    nci.call_with_err_code("make -j" + str(mp.cpu_count()))
    # build our .dmg
    nci.call_with_err_code('npm install -g appdmg')
    os.chdir("wallet")
    nci.call_with_err_code('../../contrib/macdeploy/macdeployqtplus ./neblio-Qt.app -add-qt-tr da,de,es,hu,ru,uk,zh_CN,zh_TW -verbose 4 -rpath /usr/local/opt/qt/lib')
    nci.call_with_err_code('appdmg ../../contrib/macdeploy/appdmg.json ./neblio-Qt.dmg')
    nci.call_with_err_code("ls -al")
    nci.call_with_err_code("ls -al dist")

    file_name = '$(date +%Y-%m-%d)---' + os.environ['BRANCH'] + '-' + os.environ['COMMIT'][:7] + '---neblio-Qt---macOS.zip'

    nci.call_with_err_code('zip -j ' + file_name + ' ./neblio-Qt.dmg')
    nci.call_with_err_code('mv ' + file_name + ' ' + deploy_dir)
    nci.call_with_err_code('echo "Binary package at ' + deploy_dir + file_name + '"')
    # set the SOURCE_DIR env var, this points to the binary that will be uploaded
    nci.call_with_err_code('echo "::set-env name=SOURCE_PATH::' + deploy_dir + file_name + '"')

nci.call_with_err_code('ccache -s')

print("")
print("")
print("Building finished successfully.")
print("")
<commit_msg>Update test_osx-gui_wallet.py<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import argparse
import multiprocessing as mp
import neblio_ci_libs as nci

nci.setup_travis_or_gh_actions_env_vars()

working_dir = os.getcwd()
build_dir = "build"
deploy_dir = os.path.join(os.environ['BUILD_DIR'],'deploy', '')

parser = argparse.ArgumentParser()
parser.add_argument('--test', '-t', help='Only build and run tests', action='store_true')
args = parser.parse_args()

nci.mkdir_p(deploy_dir)
nci.mkdir_p(build_dir)
os.chdir(build_dir)

# do not auto update homebrew as it is very slow
os.environ['HOMEBREW_NO_AUTO_UPDATE'] = '1'

# set up ccache
nci.call_with_err_code('brew fetch --retry ccache        && brew install ccache --force')

nci.call_with_err_code('brew fetch --retry qt            && brew install qt --force')
nci.call_with_err_code('brew fetch --retry berkeley-db@4 && brew install berkeley-db@4 --force')
nci.call_with_err_code('brew fetch --retry boost@1.60    && brew install boost@1.60 --force')
nci.call_with_err_code('brew fetch --retry miniupnpc     && brew install miniupnpc --force')
nci.call_with_err_code('brew fetch --retry curl          && brew install curl --force')
nci.call_with_err_code('brew fetch --retry openssl       && brew install openssl --force')
nci.call_with_err_code('brew fetch --retry qrencode      && brew install qrencode --force')

nci.call_with_err_code('brew unlink qt            && brew link --force --overwrite qt')
nci.call_with_err_code('brew unlink berkeley-db@4 && brew link --force --overwrite berkeley-db@4')
nci.call_with_err_code('brew unlink boost@1.60    && brew link --force --overwrite boost@1.60')
nci.call_with_err_code('brew unlink miniupnpc     && brew link --force --overwrite miniupnpc')
nci.call_with_err_code('brew unlink curl          && brew link --force --overwrite curl')
nci.call_with_err_code('brew unlink python        && brew link --force --overwrite python')
nci.call_with_err_code('brew unlink openssl       && brew link --force --overwrite openssl')
nci.call_with_err_code('brew unlink qrencode      && brew link --force --overwrite qrencode')

nci.call_with_err_code('ccache -s')

# prepend ccache to the path, necessary since prior steps prepend things to the path
os.environ['PATH'] = '/usr/local/opt/ccache/libexec:' + os.environ['PATH']

if (args.test):
    nci.call_with_err_code('qmake "QMAKE_CXX=ccache clang++" "USE_UPNP=1" "USE_QRCODE=1" "RELEASE=1" "NEBLIO_CONFIG += NoWallet" ../neblio-wallet.pro')
    nci.call_with_err_code("make -j" + str(mp.cpu_count()))
    # run tests
    nci.call_with_err_code("./wallet/test/neblio-Qt.app/Contents/MacOS/neblio-Qt")
else:
    nci.call_with_err_code('qmake "QMAKE_CXX=ccache clang++" "USE_UPNP=1" "USE_QRCODE=1" "RELEASE=1" ../neblio-wallet.pro')
    nci.call_with_err_code("make -j" + str(mp.cpu_count()))
    # build our .dmg
    nci.call_with_err_code('npm install -g appdmg')
    os.chdir("wallet")
    nci.call_with_err_code('../../contrib/macdeploy/macdeployqtplus ./neblio-Qt.app -add-qt-tr da,de,es,hu,ru,uk,zh_CN,zh_TW -verbose 4 -rpath /usr/local/opt/qt/lib')
    nci.call_with_err_code('appdmg ../../contrib/macdeploy/appdmg.json ./neblio-Qt.dmg')
    nci.call_with_err_code("ls -al")
    nci.call_with_err_code("ls -al dist")
    nci.call_with_err_code("pwd")

    file_name = '$(date +%Y-%m-%d)---' + os.environ['BRANCH'] + '-' + os.environ['COMMIT'][:7] + '---neblio-Qt---macOS.zip'

    nci.call_with_err_code('zip -j ' + file_name + ' ./neblio-Qt.dmg')
    nci.call_with_err_code('mv ' + file_name + ' ' + deploy_dir)
    nci.call_with_err_code('echo "Binary package at ' + deploy_dir + file_name + '"')
    # set the SOURCE_DIR env var, this points to the binary that will be uploaded
    nci.call_with_err_code('echo "::set-env name=SOURCE_PATH::' + deploy_dir + file_name + '"')

nci.call_with_err_code('ccache -s')

print("")
print("")
print("Building finished successfully.")
print("")
<|endoftext|>