<commit_before>"""Filter objects from a level or CustomObject."""

import os
import sys
import argparse
import re

from distance.level import Level
from distance.levelobjects import Group, GoldenSimple, OldSimple
from distance.base import BaseObject
from distance.bytes import (
    DstBytes,
    MAGIC_2, MAGIC_3, MAGIC_32, MAGIC_9,
    Section,
)
from distance.printing import PrintContext
from distance.prober import BytesProber

PROBER = BytesProber()

PROBER.add_type('Group', Group)
PROBER.add_fragment(Level, MAGIC_9)

@PROBER.func
def _detect_other(section):
    return BaseObject

MAGICMAP = {2: MAGIC_2, 3: MAGIC_3, 32: MAGIC_32}

FILTERS = {}

class ObjectFilter(object):

    @classmethod
    def add_args(cls, parser):
        parser.add_argument("--maxrecurse", type=int, default=-1,
                            help="Maximum of recursions.")

    def __init__(self, name, args):
        self.name = name
        self.maxrecurse = args.maxrecurse

    def filter_object(self, obj):
        return obj,

    def filter_group(self, grp, levels):
        orig_empty = not grp.children
        grp.children = self.filter_objects(grp.children, levels)
        if not orig_empty and not grp.children:
            # remove empty group
            return ()
        return grp,

    def filter_any_object(self, obj, levels):
        if obj.is_object_group:
            if levels == 0:
                return obj,
            return self.filter_group(obj, levels - 1)
        else:
            return self.filter_object(obj)

    def filter_objects(self, objects, levels):
        res = []
        for obj in objects:
            res.extend(self.filter_any_object(obj, levels))
        return res

    def apply_level(self, level):
        for layer in level.layers:
            layer.objects = self.filter_objects(layer.objects, self.maxrecurse)

    def apply_group(self, grp):
        # not using filter_group, because we never want to remove the root
        # group object
        grp.children = self.filter_objects(grp.children, self.maxrecurse)

    def apply(self, content):
        if isinstance(content, Level):
            self.apply_level(content)
        elif isinstance(content, Group):
            self.apply_group(content)
        else:
            raise TypeError(f'Unknown object type: {type(content).__name__!r}')

    def post_filter(self, content):
        return True

    def print_summary(self, p):
        pass

class DoNotReplace(Exception):
    pass

class OldToGsMapper(object):

    def __init__(self, type, size_factor=1, collision_only=False):
        self.type = type
        self.size_factor = size_factor
        self.collision_only = collision_only

    def apply(self, obj):
        return self._apply_default(obj)

    def _apply_default(self, obj):
        collision = obj.type.endswith('WithCollision')
        emissive = obj.type.startswith('Emissive')
        if self.collision_only and not collision:
            raise DoNotReplace
        pos, rot, scale = obj.transform or ((), (), ())
        if not scale:
            scale = (1, 1, 1)
        scale = tuple(s * self.size_factor for s in scale)
        transform = pos, rot, scale
        gs = GoldenSimple(type=self.type, transform=transform)
        if emissive:
            gs.mat_emit =  obj.color_emit
            gs.mat_reflect = (0, 0, 0, 0)
            gs.emit_index = 59
        else:
            gs.mat_color = obj.color_diffuse
            gs.image_index = 17
            gs.emit_index = 17
            gs.disable_bump = True
            gs.disable_diffuse = True
            gs.disable_reflect = True
        gs.mat_spec = (0, 0, 0, 0)
        gs.additive_transp = emissive
        gs.disable_collision = not collision
        return gs,

def create_simples_mappers():
    bugs = {
        'Cube': OldToGsMapper('CubeGS', size_factor=1/64, collision_only=True),
    }
    safe = {
        'Cube': OldToGsMapper('CubeGS', size_factor=1/64),
        'Plane': OldToGsMapper('PlaneOneSidedGS', size_factor=1/6.4),
    }
    # TODO add more mappers
    unsafe = dict(safe)
    return dict(bugs=bugs, safe=safe, unsafe=unsafe)

OLD_TO_GOLD_SIMPLES_MAPPERS = create_simples_mappers()

class GoldifyFilter(ObjectFilter):

    @classmethod
    def add_args(cls, parser):
        super().add_args(parser)
        parser.add_argument("--debug", action='store_true')
        grp = parser.add_mutually_exclusive_group()
        grp.add_argument("--bugs", action='store_const', const="bugs", dest='mode',
                         help="Replace glitched simples (CubeWithCollision).")
        grp.add_argument("--safe", action='store_const', const="safe", dest='mode',
                         help="Do all safe (exact) replacements (default).")
        grp.add_argument("--unsafe", action='store_const', const="unsafe", dest='mode',
                         help="Do all replacements.")
        grp.set_defaults(mode='safe')

    def __init__(self, name, args):
        super().__init__(name, args)
        self.mappers = OLD_TO_GOLD_SIMPLES_MAPPERS[args.mode]
        self.debug = args.debug
        self.num_replaced = 0

    def filter_object(self, obj):
        if isinstance(obj, OldSimple):
            typ = re.sub(r"^Emissive", '', obj.type)
            typ = re.sub(r"WithCollision$", '', typ)
            try:
                mapper = self.mappers[typ]
            except KeyError:
                # object not mapped in this mode
                return obj,
            try:
                result = mapper.apply(obj)
            except DoNotReplace:
                return obj,
            self.num_replaced += 1
            if self.debug:
                for res in result:
                    if res.additive_transp:
                        res.mat_emit = (1, .3, .3, .4)
                    else:
                        res.mat_color = (1, .3, .3, 1)
                return result + (obj,)
            return result
        return obj,

    def print_summary(self, p):
        p(f"Goldified simples: {self.num_replaced}")

FILTERS['goldify'] = GoldifyFilter

def parse_section(arg):
    parts = arg.split(",")
    magic = MAGICMAP[int(parts[0])]
    return Section(magic, *(int(p, base=0) for p in parts[1:]))

class RemoveFilter(ObjectFilter):

    @classmethod
    def add_args(cls, parser):
        super().add_args(parser)
        parser.add_argument("--type", action='append', default=[],
                            help="Match object type (regex).")
        parser.add_argument("--section", action='append', default=[],
                            help="Match sections.")
        parser.add_argument("--all", action='store_true',
                            help="Filter out all matching objects.")
        parser.add_argument("--number", dest='numbers', action='append',
                            type=int, default=[],
                            help="Select by candidate number.")

    def __init__(self, name, args):
        super().__init__(name, args)
        self.all = args.all
        self.numbers = args.numbers
        self.type_patterns = [re.compile(r) for r in args.type]
        self.sections = {parse_section(arg).to_key() for arg in args.section}
        self.num_matches = 0
        self.matches = []
        self.removed = []

    def _match_sections(self, obj):
        for sec in obj.sections:
            if sec.to_key() in self.sections:
                return True
        for child in obj.children:
            if self._match_sections(child):
                return True
        return False

    def match_props(self, obj):
        if not self.type_patterns and not self.sections:
            return True
        if self.type_patterns:
            typename = obj.type
            if any(r.search(typename) for r in self.type_patterns):
                return True
        if self.sections:
            if not obj.is_object_group and self._match_sections(obj):
                return True
        return False

    def match(self, obj):
        if self.match_props(obj):
            num = self.num_matches
            self.num_matches = num + 1
            self.matches.append(obj)
            if self.all:
                return True
            if num in self.numbers:
                return True
        return False

    def filter_any_object(self, obj, levels):
        remove = self.match(obj)
        res = super().filter_any_object(obj, levels)
        if remove:
            self.removed.append(obj)
            return ()
        return res

    def post_filter(self, content):
        if self.all or (self.numbers and self.matches):
            return True
        from .mkcustomobject import print_candidates
        print_candidates(self.matches)
        return False

    def print_summary(self, p):
        p(f"Removed matches: {len(self.removed)}")
        num_objs, num_groups = count_objects(self.removed)
        if num_objs != len(self.removed):
            p(f"Removed objects: {num_objs}")
            p(f"Removed groups: {num_groups}")

FILTERS['rm'] = RemoveFilter

def count_objects(objs):
    n_obj = 0
    n_grp = 0
    for obj in objs:
        n_obj += 1
        if obj.is_object_group:
            n_grp += 1
            no, ng = count_objects(obj.children)
            n_obj += no
            n_grp += ng
    return n_obj, n_grp

def make_arglist(s):

    def iter_tokens(source):
        if not source:
            return
        token = []
        escape = False
        for char in source:
            if escape:
                escape = False
                token.append(char)
            elif char == '\\':
                escape = True
            elif char == ':':
                yield token
                token = []
            else:
                token.append(char)
        yield token

    return ["--" + ''.join(token) for token in iter_tokens(s)]

def create_filter(option, defaults):
    name, sep, argstr = option.partition(':')
    cls = FILTERS[name]

    parser = argparse.ArgumentParser(prog=name)
    parser.set_defaults(**defaults)
    cls.add_args(parser)
    args = parser.parse_args(make_arglist(argstr))

    return cls(name, args)

def main():
    parser = argparse.ArgumentParser(
        description=__doc__)
    parser.add_argument("-f", "--force", action='store_true',
                        help="Allow overwriting OUT file.")
    parser.add_argument("-l", "--maxrecurse", type=int, default=-1,
                        help="Maximum of recursions. 0 only lists layer objects.")
    parser.add_argument("-o", "--of", "--objfilter", dest='objfilters',
                        action='append', default=[],
                        help="Specify a filter option.")
    parser.add_argument("--list", action='store_true',
                        help="Dump result listing.")
    parser.add_argument("IN",
                        help="Level .bytes filename.")
    parser.add_argument("OUT",
                        help="output .bytes filename.")
    args = parser.parse_args()

    defaults = dict(maxrecurse=args.maxrecurse)
    filters = [create_filter(f, defaults) for f in args.objfilters]

    write_mode = 'xb'
    if args.force:
        write_mode = 'wb'
    elif os.path.exists(args.OUT):
        print(f"file {args.OUT} exists. pass -f to force.", file=sys.stderr)
        return 1

    content = PROBER.read(args.IN)

    p = PrintContext(flags=('groups', 'subobjects'))

    for f in filters:
        f.apply(content)
        if not f.post_filter(content):
            return 1
        f.print_summary(p)

    if args.list:
        p.print_data_of(content)

    print("generating...")
    dbytes = DstBytes.in_memory()
    content.write(dbytes)

    print("writing...")
    with open(args.OUT, write_mode) as out_f:
        n = out_f.write(dbytes.file.getbuffer())

    print(f"{n} bytes written")
    return 0

if __name__ == '__main__':
    exit(main())

# vim:set sw=4 ts=8 sts=4 et sr ft=python fdm=marker tw=0:
<commit_msg>filterlevel: goldify Hexagon, Octahedron, Pyramid<commit_after>"""Filter objects from a level or CustomObject."""

import os
import sys
import argparse
import re
import collections

from distance.level import Level
from distance.levelobjects import Group, GoldenSimple, OldSimple
from distance.base import BaseObject
from distance.bytes import (
    DstBytes,
    MAGIC_2, MAGIC_3, MAGIC_32, MAGIC_9,
    Section,
)
from distance.printing import PrintContext
from distance.prober import BytesProber

PROBER = BytesProber()

PROBER.add_type('Group', Group)
PROBER.add_fragment(Level, MAGIC_9)

@PROBER.func
def _detect_other(section):
    return BaseObject

MAGICMAP = {2: MAGIC_2, 3: MAGIC_3, 32: MAGIC_32}

FILTERS = {}

class ObjectFilter(object):

    @classmethod
    def add_args(cls, parser):
        parser.add_argument("--maxrecurse", type=int, default=-1,
                            help="Maximum of recursions.")

    def __init__(self, name, args):
        self.name = name
        self.maxrecurse = args.maxrecurse

    def filter_object(self, obj):
        return obj,

    def filter_group(self, grp, levels):
        orig_empty = not grp.children
        grp.children = self.filter_objects(grp.children, levels)
        if not orig_empty and not grp.children:
            # remove empty group
            return ()
        return grp,

    def filter_any_object(self, obj, levels):
        if obj.is_object_group:
            if levels == 0:
                return obj,
            return self.filter_group(obj, levels - 1)
        else:
            return self.filter_object(obj)

    def filter_objects(self, objects, levels):
        res = []
        for obj in objects:
            res.extend(self.filter_any_object(obj, levels))
        return res

    def apply_level(self, level):
        for layer in level.layers:
            layer.objects = self.filter_objects(layer.objects, self.maxrecurse)

    def apply_group(self, grp):
        # not using filter_group, because we never want to remove the root
        # group object
        grp.children = self.filter_objects(grp.children, self.maxrecurse)

    def apply(self, content):
        if isinstance(content, Level):
            self.apply_level(content)
        elif isinstance(content, Group):
            self.apply_group(content)
        else:
            raise TypeError(f'Unknown object type: {type(content).__name__!r}')

    def post_filter(self, content):
        return True

    def print_summary(self, p):
        pass

class DoNotReplace(Exception):
    pass

class OldToGsMapper(object):

    def __init__(self, type, size_factor=1, offset=None, collision_only=False):
        self.type = type
        self.size_factor = size_factor
        self.collision_only = collision_only
        self.offset = offset

    def apply(self, obj):
        return self._apply_default(obj)

    def _apply_default(self, obj):
        collision = obj.type.endswith('WithCollision')
        emissive = obj.type.startswith('Emissive')
        if self.collision_only and not collision:
            raise DoNotReplace
        pos, rot, scale = obj.transform or ((), (), ())
        if not scale:
            scale = (1, 1, 1)
        if self.offset:
            if not pos:
                pos = (0, 0, 0)
            pos = tuple(p + q for p, q in zip(pos, self.offset(scale)))
        if isinstance(self.size_factor, collections.Sequence):
            scale = tuple(s * f for s, f in zip(scale, self.size_factor))
        else:
            scale = tuple(s * self.size_factor for s in scale)
        transform = pos, rot, scale
        gs = GoldenSimple(type=self.type, transform=transform)
        if emissive:
            gs.mat_emit =  obj.color_emit
            gs.mat_reflect = (0, 0, 0, 0)
            gs.emit_index = 59
        else:
            gs.mat_color = obj.color_diffuse
            gs.image_index = 17
            gs.emit_index = 17
            gs.disable_bump = True
            gs.disable_diffuse = True
            gs.disable_reflect = True
        gs.mat_spec = (0, 0, 0, 0)
        gs.additive_transp = emissive
        gs.disable_collision = not collision
        return gs,

def create_simples_mappers():
    bugs = {
        'Cube': OldToGsMapper('CubeGS', size_factor=1/64, collision_only=True),
    }
    safe = {
        'Cube': OldToGsMapper('CubeGS', size_factor=1/64),
        'Plane': OldToGsMapper('PlaneOneSidedGS', size_factor=1/6.4),
        'Hexagon': OldToGsMapper('HexagonGS', size_factor=(1/32, .03, 1/32)),
        'Octahedron': OldToGsMapper('OctahedronGS', size_factor=1/32),
    }
    inexact = {
        'Pyramid': OldToGsMapper('PyramidGS', size_factor=(.025898, .03867, .025898),
                                 offset=(lambda scale: (0, scale[1] * 1.23914, 0))),
        **safe,
    }
    pending = {
    }
    unsafe = dict(safe)
    unsafe.update(pending)
    return dict(bugs=bugs, safe=safe, pending=pending, inexact=inexact, unsafe=unsafe)

OLD_TO_GOLD_SIMPLES_MAPPERS = create_simples_mappers()

class GoldifyFilter(ObjectFilter):

    @classmethod
    def add_args(cls, parser):
        super().add_args(parser)
        parser.add_argument("--debug", action='store_true')
        grp = parser.add_mutually_exclusive_group()
        grp.add_argument("--bugs", action='store_const', const="bugs", dest='mode',
                         help="Replace glitched simples (CubeWithCollision).")
        grp.add_argument("--safe", action='store_const', const="safe", dest='mode',
                         help="Do all safe (exact) replacements (default).")
        grp.add_argument("--inexact", action='store_const', const="inexact", dest='mode',
                         help="Include replacements with imperfect precision.")
        grp.add_argument("--pending", action='store_const', const="pending", dest='mode',
                         help="Only use unfinished implementations (debug).")
        grp.add_argument("--unsafe", action='store_const', const="unsafe", dest='mode',
                         help="Do all replacements.")
        grp.set_defaults(mode='safe')

    def __init__(self, name, args):
        super().__init__(name, args)
        self.mappers = OLD_TO_GOLD_SIMPLES_MAPPERS[args.mode]
        self.debug = args.debug
        self.num_replaced = 0

    def filter_object(self, obj):
        if isinstance(obj, OldSimple):
            typ = re.sub(r"^Emissive", '', obj.type)
            typ = re.sub(r"WithCollision$", '', typ)
            try:
                mapper = self.mappers[typ]
            except KeyError:
                # object not mapped in this mode
                return obj,
            try:
                result = mapper.apply(obj)
            except DoNotReplace:
                return obj,
            self.num_replaced += 1
            if self.debug:
                for res in result:
                    if res.additive_transp:
                        res.mat_emit = (1, .3, .3, .4)
                    else:
                        res.mat_color = (1, .3, .3, 1)
                return result + (obj,)
            return result
        return obj,

    def print_summary(self, p):
        p(f"Goldified simples: {self.num_replaced}")

FILTERS['goldify'] = GoldifyFilter

def parse_section(arg):
    parts = arg.split(",")
    magic = MAGICMAP[int(parts[0])]
    return Section(magic, *(int(p, base=0) for p in parts[1:]))

class RemoveFilter(ObjectFilter):

    @classmethod
    def add_args(cls, parser):
        super().add_args(parser)
        parser.add_argument("--type", action='append', default=[],
                            help="Match object type (regex).")
        parser.add_argument("--section", action='append', default=[],
                            help="Match sections.")
        parser.add_argument("--all", action='store_true',
                            help="Filter out all matching objects.")
        parser.add_argument("--number", dest='numbers', action='append',
                            type=int, default=[],
                            help="Select by candidate number.")

    def __init__(self, name, args):
        super().__init__(name, args)
        self.all = args.all
        self.numbers = args.numbers
        self.type_patterns = [re.compile(r) for r in args.type]
        self.sections = {parse_section(arg).to_key() for arg in args.section}
        self.num_matches = 0
        self.matches = []
        self.removed = []

    def _match_sections(self, obj):
        for sec in obj.sections:
            if sec.to_key() in self.sections:
                return True
        for child in obj.children:
            if self._match_sections(child):
                return True
        return False

    def match_props(self, obj):
        if not self.type_patterns and not self.sections:
            return True
        if self.type_patterns:
            typename = obj.type
            if any(r.search(typename) for r in self.type_patterns):
                return True
        if self.sections:
            if not obj.is_object_group and self._match_sections(obj):
                return True
        return False

    def match(self, obj):
        if self.match_props(obj):
            num = self.num_matches
            self.num_matches = num + 1
            self.matches.append(obj)
            if self.all:
                return True
            if num in self.numbers:
                return True
        return False

    def filter_any_object(self, obj, levels):
        remove = self.match(obj)
        res = super().filter_any_object(obj, levels)
        if remove:
            self.removed.append(obj)
            return ()
        return res

    def post_filter(self, content):
        if self.all or (self.numbers and self.matches):
            return True
        from .mkcustomobject import print_candidates
        print_candidates(self.matches)
        return False

    def print_summary(self, p):
        p(f"Removed matches: {len(self.removed)}")
        num_objs, num_groups = count_objects(self.removed)
        if num_objs != len(self.removed):
            p(f"Removed objects: {num_objs}")
            p(f"Removed groups: {num_groups}")

FILTERS['rm'] = RemoveFilter

def count_objects(objs):
    n_obj = 0
    n_grp = 0
    for obj in objs:
        n_obj += 1
        if obj.is_object_group:
            n_grp += 1
            no, ng = count_objects(obj.children)
            n_obj += no
            n_grp += ng
    return n_obj, n_grp

def make_arglist(s):

    def iter_tokens(source):
        if not source:
            return
        token = []
        escape = False
        for char in source:
            if escape:
                escape = False
                token.append(char)
            elif char == '\\':
                escape = True
            elif char == ':':
                yield token
                token = []
            else:
                token.append(char)
        yield token

    return ["--" + ''.join(token) for token in iter_tokens(s)]

def create_filter(option, defaults):
    name, sep, argstr = option.partition(':')
    cls = FILTERS[name]

    parser = argparse.ArgumentParser(prog=name)
    parser.set_defaults(**defaults)
    cls.add_args(parser)
    args = parser.parse_args(make_arglist(argstr))

    return cls(name, args)

def main():
    parser = argparse.ArgumentParser(
        description=__doc__)
    parser.add_argument("-f", "--force", action='store_true',
                        help="Allow overwriting OUT file.")
    parser.add_argument("-l", "--maxrecurse", type=int, default=-1,
                        help="Maximum of recursions. 0 only lists layer objects.")
    parser.add_argument("-o", "--of", "--objfilter", dest='objfilters',
                        action='append', default=[],
                        help="Specify a filter option.")
    parser.add_argument("--list", action='store_true',
                        help="Dump result listing.")
    parser.add_argument("IN",
                        help="Level .bytes filename.")
    parser.add_argument("OUT",
                        help="output .bytes filename.")
    args = parser.parse_args()

    defaults = dict(maxrecurse=args.maxrecurse)
    filters = [create_filter(f, defaults) for f in args.objfilters]

    write_mode = 'xb'
    if args.force:
        write_mode = 'wb'
    elif os.path.exists(args.OUT):
        print(f"file {args.OUT} exists. pass -f to force.", file=sys.stderr)
        return 1

    content = PROBER.read(args.IN)

    p = PrintContext(flags=('groups', 'subobjects'))

    for f in filters:
        f.apply(content)
        if not f.post_filter(content):
            return 1
        f.print_summary(p)

    if args.list:
        p.print_data_of(content)

    print("generating...")
    dbytes = DstBytes.in_memory()
    content.write(dbytes)

    print("writing...")
    with open(args.OUT, write_mode) as out_f:
        n = out_f.write(dbytes.file.getbuffer())

    print(f"{n} bytes written")
    return 0

if __name__ == '__main__':
    exit(main())

# vim:set sw=4 ts=8 sts=4 et sr ft=python fdm=marker tw=0:
<|endoftext|>


<commit_before>import inspect

import yaml
from aiohttp.web import UrlDispatcher
from djaio_swagger.transmute import DjaioTransmuteFunction
from transmute_core import default_context
from transmute_core import describe

class TransmuteUrlDispatcher(UrlDispatcher):
    """
    A UrlDispatcher which instruments the add_route function to
    collect swagger spec data from transmuted functions.
    """

    METHODS = ('get', 'post', 'put', 'delete')

    def __init__(self, *args, context=default_context, **kwargs):
        super().__init__()
        self._transmute_context = context
        self._swagger = {}

    def get_methods_swagger_doc(self, handler):
        doc = getattr(handler, '__doc__')
        return yaml.load(doc) if doc else {}

    def add_to_swagger(self, handler, path):
        for m in self.METHODS:
            method_func = getattr(handler, '{}_method'.format(m), None)
            if method_func:
                describe(methods=m, paths=path)(handler)
                transmute_func = DjaioTransmuteFunction(handler, m, method_func, args_not_from_request=["request"])
                swagger_path = transmute_func.get_swagger_path(self._transmute_context)
                #add to swagger
                if path not in self._swagger:
                    self._swagger[path] = swagger_path
                setattr(self._swagger[path], m, swagger_path.to_native().get(m))

    def add_route(self, methods, path, handler, *, name=None, expect_handler=None):
        """
        Replace a base add_route to own for ClassBasedViews.
        """
        if inspect.isclass(handler):
            resource = self.add_resource(path, name=name)
            if type(methods) == str:
                methods = [methods]
            for m in methods:
                resource.add_route(m, handler, expect_handler=expect_handler)
            self.add_to_swagger(handler, path)
        else:
            super().add_route(methods, path, handler, name=name,expect_handler=expect_handler)

    def swagger_paths(self):
        """
        returns a swagger Paths object representing all transmute
        functions registered.
        """
        return self._swagger<commit_msg>Fixed super<commit_after>import inspect

import yaml
from aiohttp.web import UrlDispatcher
from djaio_swagger.transmute import DjaioTransmuteFunction
from transmute_core import default_context
from transmute_core import describe

class TransmuteUrlDispatcher(UrlDispatcher):
    """
    A UrlDispatcher which instruments the add_route function to
    collect swagger spec data from transmuted functions.
    """

    METHODS = ('get', 'post', 'put', 'delete')

    def __init__(self, *args, context=default_context, **kwargs):
        super(TransmuteUrlDispatcher, self).__init__()
        self._transmute_context = context
        self._swagger = {}

    def get_methods_swagger_doc(self, handler):
        doc = getattr(handler, '__doc__')
        return yaml.load(doc) if doc else {}

    def add_to_swagger(self, handler, path):
        for m in self.METHODS:
            method_func = getattr(handler, '{}_method'.format(m), None)
            if method_func:
                describe(methods=m, paths=path)(handler)
                transmute_func = DjaioTransmuteFunction(handler, m, method_func, args_not_from_request=["request"])
                swagger_path = transmute_func.get_swagger_path(self._transmute_context)
                #add to swagger
                if path not in self._swagger:
                    self._swagger[path] = swagger_path
                setattr(self._swagger[path], m, swagger_path.to_native().get(m))

    def add_route(self, methods, path, handler, *, name=None, expect_handler=None):
        """
        Replace a base add_route to own for ClassBasedViews.
        """
        if inspect.isclass(handler):
            resource = self.add_resource(path, name=name)
            if type(methods) == str:
                methods = [methods]
            for m in methods:
                resource.add_route(m, handler, expect_handler=expect_handler)
            self.add_to_swagger(handler, path)
        else:
            super().add_route(methods, path, handler, name=name,expect_handler=expect_handler)

    def swagger_paths(self):
        """
        returns a swagger Paths object representing all transmute
        functions registered.
        """
        return self._swagger
<|endoftext|>


<commit_before># Copyright 2015 Google Inc. 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.

""" Define API Subscriptions."""

from gcloud.exceptions import NotFound

class Subscription(object):
    """Subscriptions receive messages published to their topics.

    See:
    https://cloud.google.com/pubsub/reference/rest/v1beta2/projects/subscriptions

    :type name: string
    :param name: the name of the subscription

    :type topic: :class:`gcloud.pubsub.topic.Topic`
    :param topic: the topic to which the subscription belongs..

    :type ack_deadline: int
    :param ack_deadline: the deadline (in seconds) by which messages pulled
                         from the back-end must be acknowledged.

    :type push_endpoint: string
    :param push_endpoint: URL to which messages will be pushed by the back-end.
                          If not set, the application must pull messages.
    """
    def __init__(self, name, topic, ack_deadline=None, push_endpoint=None):
        self.name = name
        self.topic = topic
        self.ack_deadline = ack_deadline
        self.push_endpoint = push_endpoint

    @property
    def path(self):
        """URL path for the subscription's APIs"""
        project = self.topic.project
        return '/projects/%s/subscriptions/%s' % (project, self.name)

    def create(self):
        """API call:  create the subscription via a PUT request

        See:
        https://cloud.google.com/pubsub/reference/rest/v1beta2/projects/subscriptions/create
        """
        data = {'topic': self.topic.path}

        if self.ack_deadline is not None:
            data['ackDeadline'] = self.ack_deadline

        if self.push_endpoint is not None:
            data['pushConfig'] = {'pushEndpoint': self.push_endpoint}

        conn = self.topic.connection
        conn.api_request(method='PUT', path=self.path, data=data)

    def exists(self):
        """API call:  test existence of the subscription via a GET request

        See
        https://cloud.google.com/pubsub/reference/rest/v1beta2/projects/subscriptions/get
        """
        conn = self.topic.connection
        try:
            conn.api_request(method='GET',
                             path=self.path,
                             query_params={'fields': 'name'})
        except NotFound:
            return False
        else:
            return True

    def reload(self):
        """API call:  sync local subscription configuration via a GET request

        See
        https://cloud.google.com/pubsub/reference/rest/v1beta2/projects/subscriptions/get
        """
        conn = self.topic.connection
        data = conn.api_request(method='GET', path=self.path)
        self.ack_deadline = data.get('ackDeadline')
        push_config = data.get('pushConfig', {})
        self.push_endpoint = push_config.get('pushEndpoint')

    def modify_push_configuration(self, push_endpoint):
        """API call:  update the push endpoint for the subscription.

        See:
        https://cloud.google.com/pubsub/reference/rest/v1beta2/projects/subscriptions/modifyPushConfig

        :type push_endpoint: string
        :param push_endpoint: URL to which messages will be pushed by the
                              back-end.  If None, the application must pull
                              messages.
        """
        data = {}
        config = data['pushConfig'] = {}
        if push_endpoint is not None:
            config['pushEndpoint'] = push_endpoint
        conn = self.topic.connection
        conn.api_request(method='POST',
                         path='%s:modifyPushConfig' % self.path,
                         data=data)
        self.push_endpoint = push_endpoint

    def pull(self, return_immediately=False, max_messages=1):
        """API call:  retrieve messages for the subscription.

        See:
        https://cloud.google.com/pubsub/reference/rest/v1beta2/projects/subscriptions/pull

        :type return_immediately: boolean
        :param return_immediately: if True, the back-end returns even if no
                                   messages are available;  if False, the API
                                   call blocks until one or more messages are
                                   available.

        :type max_messages: int
        :param max_messages: the maximum number of messages to return.

        :rtype: list of dict
        :returns: sequence of mappings, each containing keys ``ackId`` (the
                  ID to be used in a subsequent call to :meth:`acknowledge`)
                  and ``message``.
        """
        data = {'returnImmediately': return_immediately,
                'maxMessages': max_messages}
        conn = self.topic.connection
        response = conn.api_request(method='POST',
                                    path='%s:pull' % self.path,
                                    data=data)
        return response['receivedMessages']

    def acknowledge(self, ack_ids):
        """API call:  acknowledge retrieved messages for the subscription.

        See:
        https://cloud.google.com/pubsub/reference/rest/v1beta2/projects/subscriptions/acknowledge

        :type ack_ids: list of string
        :param ack_ids: ack IDs of messages being acknowledged
        """
        data = {'ackIds': ack_ids}
        conn = self.topic.connection
        conn.api_request(method='POST',
                         path='%s:acknowledge' % self.path,
                         data=data)

    def modify_ack_deadline(self, ack_id, ack_deadline):
        """API call:  update acknowledgement deadline for a retrieved messages.

        See:
        https://cloud.google.com/pubsub/reference/rest/v1beta2/projects/subscriptions/acknowledge

        :type ack_id: string
        :param ack_id: ack ID of message being updated

        :type ack_deadline: int
        :param ack_deadline: new deadline for the message, in seconds
        """
        data = {'ackId': ack_id, 'ackDeadlineSeconds': ack_deadline}
        conn = self.topic.connection
        conn.api_request(method='POST',
                         path='%s:modifyAckDeadline' % self.path,
                         data=data)

    def delete(self):
        """API call:  delete the subscription via a DELETE request

        See:
        https://cloud.google.com/pubsub/reference/rest/v1beta2/projects/subscriptions/delete
        """
        conn = self.topic.connection
        conn.api_request(method='DELETE', path=self.path)
<commit_msg>Docstring typo fixes per @tmatsuo.<commit_after># Copyright 2015 Google Inc. 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.

""" Define API Subscriptions."""

from gcloud.exceptions import NotFound

class Subscription(object):
    """Subscriptions receive messages published to their topics.

    See:
    https://cloud.google.com/pubsub/reference/rest/v1beta2/projects/subscriptions

    :type name: string
    :param name: the name of the subscription

    :type topic: :class:`gcloud.pubsub.topic.Topic`
    :param topic: the topic to which the subscription belongs..

    :type ack_deadline: int
    :param ack_deadline: the deadline (in seconds) by which messages pulled
                         from the back-end must be acknowledged.

    :type push_endpoint: string
    :param push_endpoint: URL to which messages will be pushed by the back-end.
                          If not set, the application must pull messages.
    """
    def __init__(self, name, topic, ack_deadline=None, push_endpoint=None):
        self.name = name
        self.topic = topic
        self.ack_deadline = ack_deadline
        self.push_endpoint = push_endpoint

    @property
    def path(self):
        """URL path for the subscription's APIs"""
        project = self.topic.project
        return '/projects/%s/subscriptions/%s' % (project, self.name)

    def create(self):
        """API call:  create the subscription via a PUT request

        See:
        https://cloud.google.com/pubsub/reference/rest/v1beta2/projects/subscriptions/create
        """
        data = {'topic': self.topic.path}

        if self.ack_deadline is not None:
            data['ackDeadline'] = self.ack_deadline

        if self.push_endpoint is not None:
            data['pushConfig'] = {'pushEndpoint': self.push_endpoint}

        conn = self.topic.connection
        conn.api_request(method='PUT', path=self.path, data=data)

    def exists(self):
        """API call:  test existence of the subscription via a GET request

        See
        https://cloud.google.com/pubsub/reference/rest/v1beta2/projects/subscriptions/get
        """
        conn = self.topic.connection
        try:
            conn.api_request(method='GET',
                             path=self.path,
                             query_params={'fields': 'name'})
        except NotFound:
            return False
        else:
            return True

    def reload(self):
        """API call:  sync local subscription configuration via a GET request

        See
        https://cloud.google.com/pubsub/reference/rest/v1beta2/projects/subscriptions/get
        """
        conn = self.topic.connection
        data = conn.api_request(method='GET', path=self.path)
        self.ack_deadline = data.get('ackDeadline')
        push_config = data.get('pushConfig', {})
        self.push_endpoint = push_config.get('pushEndpoint')

    def modify_push_configuration(self, push_endpoint):
        """API call:  update the push endpoint for the subscription.

        See:
        https://cloud.google.com/pubsub/reference/rest/v1beta2/projects/subscriptions/modifyPushConfig

        :type push_endpoint: string
        :param push_endpoint: URL to which messages will be pushed by the
                              back-end.  If None, the application must pull
                              messages.
        """
        data = {}
        config = data['pushConfig'] = {}
        if push_endpoint is not None:
            config['pushEndpoint'] = push_endpoint
        conn = self.topic.connection
        conn.api_request(method='POST',
                         path='%s:modifyPushConfig' % self.path,
                         data=data)
        self.push_endpoint = push_endpoint

    def pull(self, return_immediately=False, max_messages=1):
        """API call:  retrieve messages for the subscription.

        See:
        https://cloud.google.com/pubsub/reference/rest/v1beta2/projects/subscriptions/pull

        :type return_immediately: boolean
        :param return_immediately: if True, the back-end returns even if no
                                   messages are available;  if False, the API
                                   call blocks until one or more messages are
                                   available.

        :type max_messages: int
        :param max_messages: the maximum number of messages to return.

        :rtype: list of dict
        :returns: sequence of mappings, each containing keys ``ackId`` (the
                  ID to be used in a subsequent call to :meth:`acknowledge`)
                  and ``message``.
        """
        data = {'returnImmediately': return_immediately,
                'maxMessages': max_messages}
        conn = self.topic.connection
        response = conn.api_request(method='POST',
                                    path='%s:pull' % self.path,
                                    data=data)
        return response['receivedMessages']

    def acknowledge(self, ack_ids):
        """API call:  acknowledge retrieved messages for the subscription.

        See:
        https://cloud.google.com/pubsub/reference/rest/v1beta2/projects/subscriptions/acknowledge

        :type ack_ids: list of string
        :param ack_ids: ack IDs of messages being acknowledged
        """
        data = {'ackIds': ack_ids}
        conn = self.topic.connection
        conn.api_request(method='POST',
                         path='%s:acknowledge' % self.path,
                         data=data)

    def modify_ack_deadline(self, ack_id, ack_deadline):
        """API call:  update acknowledgement deadline for a retrieved message.

        See:
        https://cloud.google.com/pubsub/reference/rest/v1beta2/projects/subscriptions/acknowledge

        :type ack_id: string
        :param ack_id: ack ID of message being updated

        :type ack_deadline: int
        :param ack_deadline: new deadline for the message, in seconds
        """
        data = {'ackId': ack_id, 'ackDeadlineSeconds': ack_deadline}
        conn = self.topic.connection
        conn.api_request(method='POST',
                         path='%s:modifyAckDeadline' % self.path,
                         data=data)

    def delete(self):
        """API call:  delete the subscription via a DELETE request.

        See:
        https://cloud.google.com/pubsub/reference/rest/v1beta2/projects/subscriptions/delete
        """
        conn = self.topic.connection
        conn.api_request(method='DELETE', path=self.path)
<|endoftext|>


<commit_before>from django.core.exceptions import ImproperlyConfigured

SESSION_KEY = '_auth_user_id'
BACKEND_SESSION_KEY = '_auth_user_backend'
LOGIN_URL = '/accounts/login/'
REDIRECT_FIELD_NAME = 'next'

def load_backend(path):
    i = path.rfind('.')
    module, attr = path[:i], path[i+1:]
    try:
        mod = __import__(module, '', '', [attr])
    except ImportError, e:
        raise ImproperlyConfigured, 'Error importing authentication backend %s: "%s"' % (module, e)
    try:
        cls = getattr(mod, attr)
    except AttributeError:
        raise ImproperlyConfigured, 'Module "%s" does not define a "%s" authentication backend' % (module, attr)
    return cls()

def get_backends():
    from django.conf import settings
    backends = []
    for backend_path in settings.AUTHENTICATION_BACKENDS:
        backends.append(load_backend(backend_path))
    return backends

def authenticate(**credentials):
    """
    If the given credentials are valid, return a User object.
    """
    for backend in get_backends():
        try:
            user = backend.authenticate(**credentials)
        except TypeError:
            # This backend doesn't accept these credentials as arguments. Try the next one.
            continue
        if user is None:
            continue
        # Annotate the user object with the path of the backend.
        user.backend = str(backend.__class__)
        return user

def login(request, user):
    """
    Persist a user id and a backend in the request. This way a user doesn't
    have to reauthenticate on every request.
    """
    if user is None:
        user = request.user
    # TODO: It would be nice to support different login methods, like signed cookies.
    request.session[SESSION_KEY] = user.id
    request.session[BACKEND_SESSION_KEY] = user.backend

def logout(request):
    """
    Remove the authenticated user's ID from the request.
    """
    del request.session[SESSION_KEY]
    del request.session[BACKEND_SESSION_KEY]

def get_user(request):
    from django.contrib.auth.models import AnonymousUser
    try:
        user_id = request.session[SESSION_KEY]
        backend_path = request.session[BACKEND_SESSION_KEY]
        backend = load_backend(backend_path)
        user = backend.get_user(user_id) or AnonymousUser()
    except KeyError:
        user = AnonymousUser()
    return user
<commit_msg>Fixed #2325 -- fix the authentication framework to work with new-style classes. Patch from Joseph Kocherhans.<commit_after>from django.core.exceptions import ImproperlyConfigured

SESSION_KEY = '_auth_user_id'
BACKEND_SESSION_KEY = '_auth_user_backend'
LOGIN_URL = '/accounts/login/'
REDIRECT_FIELD_NAME = 'next'

def load_backend(path):
    i = path.rfind('.')
    module, attr = path[:i], path[i+1:]
    try:
        mod = __import__(module, '', '', [attr])
    except ImportError, e:
        raise ImproperlyConfigured, 'Error importing authentication backend %s: "%s"' % (module, e)
    try:
        cls = getattr(mod, attr)
    except AttributeError:
        raise ImproperlyConfigured, 'Module "%s" does not define a "%s" authentication backend' % (module, attr)
    return cls()

def get_backends():
    from django.conf import settings
    backends = []
    for backend_path in settings.AUTHENTICATION_BACKENDS:
        backends.append(load_backend(backend_path))
    return backends

def authenticate(**credentials):
    """
    If the given credentials are valid, return a User object.
    """
    for backend in get_backends():
        try:
            user = backend.authenticate(**credentials)
        except TypeError:
            # This backend doesn't accept these credentials as arguments. Try the next one.
            continue
        if user is None:
            continue
        # Annotate the user object with the path of the backend.
        user.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__)
        return user

def login(request, user):
    """
    Persist a user id and a backend in the request. This way a user doesn't
    have to reauthenticate on every request.
    """
    if user is None:
        user = request.user
    # TODO: It would be nice to support different login methods, like signed cookies.
    request.session[SESSION_KEY] = user.id
    request.session[BACKEND_SESSION_KEY] = user.backend

def logout(request):
    """
    Remove the authenticated user's ID from the request.
    """
    del request.session[SESSION_KEY]
    del request.session[BACKEND_SESSION_KEY]

def get_user(request):
    from django.contrib.auth.models import AnonymousUser
    try:
        user_id = request.session[SESSION_KEY]
        backend_path = request.session[BACKEND_SESSION_KEY]
        backend = load_backend(backend_path)
        user = backend.get_user(user_id) or AnonymousUser()
    except KeyError:
        user = AnonymousUser()
    return user
<|endoftext|>


<commit_before>import json

from django import forms
from django.urls import reverse

class FineUploaderWidget(forms.MultipleHiddenInput):
    template_name = 'django_fine_uploader/widget.html'

    def __init__(self, attrs=None, **kwargs):
        self.options = kwargs.pop('options', {})

        self.allow_delete = kwargs.pop('allow_delete', True)
        self.allow_retry = kwargs.pop('allow_retry', True)
        self.drop_label = kwargs.get('drop_label', 'Or just drag and drop file(s) here')
        self.upload_label = kwargs.get('upload_label', 'Select file(s)')
        self.delete_label = kwargs.pop('delete_label', 'Delete')
        self.retry_label = kwargs.pop('retry_label', 'Retry')
        self.pause_label = kwargs.pop('pause_label', 'Pause')
        self.continue_label = kwargs.pop('continue_label', 'Continue')
        self.close_label = kwargs.get('close_label', 'Close')
        self.yes_label = kwargs.get('yes_label', 'Yes')
        self.no_label = kwargs.get('no_label', 'No')
        self.ok_label = kwargs.get('ok_label', 'OK')
        self.cancel_label = kwargs.get('cancel_label', 'Cancel')

        self.include_media = kwargs.pop('include_media', True)
        super(FineUploaderWidget, self).__init__(attrs, **kwargs)
        self.type = 'hidden'

    def get_context(self, name, value, attrs):
        context = super(FineUploaderWidget, self).get_context(name, value, attrs)
        upload_url = reverse('django_fine_uploader:upload')
        options = {
            'request': {
                'endpoint': upload_url,
            },
            'deleteFile': {
                'enabled': True,
                'endpoint':  reverse('django_fine_uploader:delete'),
            },
            'chunking': {
                'enabled': True,
                'concurrent': {
                    'enabled': True
                },
                'success': {
                    'endpoint': '%s?done' % upload_url
                }
            }
        }
        options.update(self.options)
        context.update({
            'options': json.dumps(options),
            'allow_delete': self.allow_delete,
            'allow_retry': self.allow_retry,
            'drop_label': self.drop_label,
            'upload_label': self.upload_label,
            'delete_label': self.delete_label,
            'retry_label': self.retry_label,
            'pause_label': self.pause_label,
            'continue_label': self.continue_label,
            'close_label': self.close_label,
            'yes_label': self.yes_label,
            'no_label': self.no_label,
            'ok_label': self.ok_label,
            'cancel_label': self.cancel_label,
        })
        return context

    @property
    def media(self):
        if not self.include_media:
            return forms.Media()
        return forms.Media(
            css={
                'all': ('django_fine_uploader/fine-uploader-gallery.min.css',)
            },
            js=(
                'django_fine_uploader/fine-uploader.min.js',
                'django_fine_uploader/js.cookie.min.js'
            )
        )
<commit_msg>Added possibility to include or not css and js files when loading the widget.<commit_after>import json

from django import forms
from django.urls import reverse

class FineUploaderWidget(forms.MultipleHiddenInput):
    template_name = 'django_fine_uploader/widget.html'

    def __init__(self, attrs=None, **kwargs):
        self.options = kwargs.pop('options', {})

        self.allow_delete = kwargs.pop('allow_delete', True)
        self.allow_retry = kwargs.pop('allow_retry', True)
        self.drop_label = kwargs.get('drop_label', 'Or just drag and drop file(s) here')
        self.upload_label = kwargs.get('upload_label', 'Select file(s)')
        self.delete_label = kwargs.pop('delete_label', 'Delete')
        self.retry_label = kwargs.pop('retry_label', 'Retry')
        self.pause_label = kwargs.pop('pause_label', 'Pause')
        self.continue_label = kwargs.pop('continue_label', 'Continue')
        self.close_label = kwargs.get('close_label', 'Close')
        self.yes_label = kwargs.get('yes_label', 'Yes')
        self.no_label = kwargs.get('no_label', 'No')
        self.ok_label = kwargs.get('ok_label', 'OK')
        self.cancel_label = kwargs.get('cancel_label', 'Cancel')

        self.include_js = kwargs.pop('include_js', True)
        self.include_css = kwargs.pop('include_css', True)
        super(FineUploaderWidget, self).__init__(attrs, **kwargs)
        self.type = 'hidden'

    def get_context(self, name, value, attrs):
        context = super(FineUploaderWidget, self).get_context(name, value, attrs)
        upload_url = reverse('django_fine_uploader:upload')
        options = {
            'request': {
                'endpoint': upload_url,
            },
            'deleteFile': {
                'enabled': True,
                'endpoint':  reverse('django_fine_uploader:delete'),
            },
            'chunking': {
                'enabled': True,
                'concurrent': {
                    'enabled': True
                },
                'success': {
                    'endpoint': '%s?done' % upload_url
                }
            }
        }
        options.update(self.options)
        context.update({
            'options': json.dumps(options),
            'allow_delete': self.allow_delete,
            'allow_retry': self.allow_retry,
            'drop_label': self.drop_label,
            'upload_label': self.upload_label,
            'delete_label': self.delete_label,
            'retry_label': self.retry_label,
            'pause_label': self.pause_label,
            'continue_label': self.continue_label,
            'close_label': self.close_label,
            'yes_label': self.yes_label,
            'no_label': self.no_label,
            'ok_label': self.ok_label,
            'cancel_label': self.cancel_label,
        })
        return context

    @property
    def media(self):
        kwargs = {}
        if self.include_js:
            kwargs['js'] = (
                'django_fine_uploader/js.cookie.min.js',
                'django_fine_uploader/fine-uploader.min.js',
            )
        if self.include_css:
            kwargs['css'] = {
                'all': ('django_fine_uploader/fine-uploader-gallery.min.css',)
            }
        return forms.Media(**kwargs)
<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'Iacopo Spalletti'
__email__ = 'i.spalletti@nephila.it'
__version__ = '0.7.8dev1'
<commit_msg>Bump version<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'Iacopo Spalletti'
__email__ = 'i.spalletti@nephila.it'
__version__ = '0.7.8'
<|endoftext|>


<commit_before>import errno
import os
import re
import subprocess
import sys
from subprocess import Popen

from dmoj.error import CompileError, InternalError
from dmoj.executors.compiled_executor import CompiledExecutor
from dmoj.result import Result
from dmoj.utils.unicode import utf8bytes, utf8text

recomment = re.compile(r'/\*.*?\*/', re.DOTALL | re.U)
restring = re.compile(r''''(?:\\.|[^'\\])'|"(?:\\.|[^"\\])*"''', re.DOTALL | re.U)
reinline_comment = re.compile(r'//.*?(?=[\r\n])', re.U)
reclass = re.compile(r'\bpublic\s+(?:strictfp\s+)?(?:(?:abstract|final)\s+)?(?:strictfp\s+)?class\s+([\w\$][\w\$]*?)\b',
                     re.U)
repackage = re.compile(r'\bpackage\s+([^.;]+(?:\.[^.;]+)*?);', re.U)

JAVA_SANDBOX = os.path.abspath(os.path.join(os.path.dirname(__file__), 'java_sandbox.jar'))

POLICY_PREFIX = '''\
grant codeBase "file:///{agent}" {{
    permission java.io.FilePermission "state", "write";
}};

'''

with open(os.path.join(os.path.dirname(__file__), 'java-security.policy')) as policy_file:
    policy = policy_file.read()

def find_class(source):
    source = reinline_comment.sub('', restring.sub('', recomment.sub('', source)))
    class_name = reclass.search(source)
    if class_name is None:
        raise CompileError('No public class: your main class must be declared as a "public class"\n')
    package = repackage.search(source)
    if package:
        raise CompileError('Invalid package %s: do not declare package\n' % package.group(1))
    return class_name

class JavaExecutor(CompiledExecutor):
    ext = 'java'

    vm = None
    compiler = None
    nproc = -1
    fsize = 64  # Allow 64 bytes for dumping state file.
    address_grace = 786432

    jvm_regex = None
    security_policy = policy

    def __init__(self, problem_id, source_code, **kwargs):
        self._class_name = None
        super(JavaExecutor, self).__init__(problem_id, source_code, **kwargs)

    def create_files(self, problem_id, source_code, *args, **kwargs):
        super(JavaExecutor, self).create_files(problem_id, source_code, *args, **kwargs)

        self._agent_file = JAVA_SANDBOX
        self._policy_file = self._file('security.policy')
        with open(self._policy_file, 'w') as file:
            file.write(POLICY_PREFIX.format(agent=self._agent_file) + self.security_policy)

    def get_compile_popen_kwargs(self):
        return {'executable': self.get_compiler()}

    def get_compiled_file(self):
        return None

    def get_security(self, launch_kwargs=None):
        return None

    def get_executable(self):
        return self.get_vm()

    def get_cmdline(self):
        agent_flags = '-javaagent:%s=policy:%s' % (self._agent_file, self._policy_file)
        for hint in self._hints:
            agent_flags += ',%s' % hint
        if self.unbuffered:
            agent_flags += ',nobuf'
        # 128m is equivalent to 1<<27 in Thread constructor
        return ['java', self.get_vm_mode(), agent_flags, '-Xss128m', '-Xmx%dK' % self.__memory_limit,
                '-XX:+UseSerialGC', '-XX:ErrorFile=submission_jvm_crash.log', self._class_name]

    def launch(self, *args, **kwargs):
        self.__memory_limit = kwargs['memory']
        kwargs['memory'] = 0
        return super(JavaExecutor, self).launch(*args, **kwargs)

    def launch_unsafe(self, *args, **kwargs):
        return Popen(['java', self.get_vm_mode(), self._class_name] + list(args),
                     executable=self.get_vm(), cwd=self._dir, **kwargs)

    def get_feedback(self, stderr, result, process):
        if process.returncode:
            try:
                with open(os.path.join(self._dir, 'submission_jvm_crash.log'), 'r') as err:
                    raise InternalError('\n\n' + err.read())
            except IOError:
                pass

        if not result.result_flag & Result.IR:
            return ''

        if b'Error: Main method not found in class' in stderr:
            exception = "public static void main(String[] args) not found"
        else:
            try:
                with open(os.path.join(self._dir, 'state'), 'r') as state:
                    exception = state.read().strip()
            except IOError:
                exception = "abnormal termination"  # Probably exited without calling shutdown hooks

        return exception

    @classmethod
    def get_vm(cls):
        return cls.runtime_dict.get(cls.vm)

    @classmethod
    def get_vm_mode(cls):
        return '-%s' % cls.runtime_dict.get(cls.vm + '_mode', 'client')

    @classmethod
    def get_compiler(cls):
        return cls.runtime_dict.get(cls.compiler)

    @classmethod
    def initialize(cls, sandbox=True):
        if cls.get_vm() is None or cls.get_compiler() is None:
            return False
        if not os.path.isfile(cls.get_vm()) or not os.path.isfile(cls.get_compiler()):
            return False
        return cls.run_self_test(sandbox)

    @classmethod
    def test_jvm(cls, name, path):
        raise NotImplementedError()

    @classmethod
    def get_versionable_commands(cls):
        return [('javac', cls.get_compiler())]

    @classmethod
    def get_version_flags(cls, command):
        return ['-version']

    @classmethod
    def autoconfig(cls):
        if cls.jvm_regex is None:
            return {}, False, 'Unimplemented'

        JVM_DIR = '/usr/local' if sys.platform.startswith('freebsd') else '/usr/lib/jvm'
        regex = re.compile(cls.jvm_regex)

        try:
            vms = os.listdir(JVM_DIR)
        except OSError:
            vms = []

        for item in vms:
            path = os.path.join(JVM_DIR, item)
            if not os.path.isdir(path) or os.path.islink(path):
                continue

            if regex.match(item):
                try:
                    config, success, message = cls.test_jvm(item, path)
                except (NotImplementedError, TypeError, ValueError):
                    return {}, False, 'Unimplemented'
                else:
                    if success:
                        return config, success, message
        return {}, False, 'Could not find JVM'

    @classmethod
    def unravel_java(cls, path):
        with open(path, 'rb') as f:
            if f.read(2) != '#!':
                return path

        with open(os.devnull, 'w') as devnull:
            process = subprocess.Popen(['bash', '-x', path, '-version'], stdout=devnull, stderr=subprocess.PIPE)

        log = [i for i in process.communicate()[1].split('\n') if 'exec' in i]
        cmdline = log[-1].lstrip('+ ').split()
        return cmdline[1] if len(cmdline) > 1 else path

class JavacExecutor(JavaExecutor):
    def create_files(self, problem_id, source_code, *args, **kwargs):
        super(JavacExecutor, self).create_files(problem_id, source_code, *args, **kwargs)
        # This step is necessary because of Unicode classnames
        try:
            source_code = utf8text(source_code)
        except UnicodeDecodeError:
            raise CompileError('Your UTF-8 is bad, and you should feel bad')
        class_name = find_class(source_code)
        self._code = self._file('%s.java' % class_name.group(1))
        try:
            with open(self._code, 'wb') as fo:
                fo.write(utf8bytes(source_code))
        except IOError as e:
            if e.errno in (errno.ENAMETOOLONG, errno.ENOENT, errno.EINVAL):
                raise CompileError('Why do you need a class name so long? '
                                   'As a judge, I sentence your code to death.\n')
            raise
        self._class_name = class_name.group(1)

    def get_compile_args(self):
        return [self.get_compiler(), '-Xlint', '-encoding', 'UTF-8', self._code]

    def handle_compile_error(self, output):
        if b'is public, should be declared in a file named' in utf8bytes(output):
            raise CompileError('You are a troll. Trolls are not welcome. '
                               'As a judge, I sentence your code to death.\n')
        raise CompileError(output)

    @classmethod
    def test_jvm(cls, name, path):
        vm_path = os.path.join(path, 'bin', 'java')
        compiler_path = os.path.join(path, 'bin', 'javac')

        if os.path.isfile(vm_path) and os.path.isfile(compiler_path):
            # Not all JVMs have the same VMs available; specifically,
            # OpenJDK for ARM has no client VM, but has dcevm+server. So, we test
            # a bunch and if it's not the default (-client), then we list it
            # in the config.
            vm_modes = ['client', 'server', 'dcevm', 'zero']
            cls_vm_mode = cls.vm + '_mode'
            for mode in vm_modes:
                result = {cls.vm: vm_path, cls_vm_mode: mode, cls.compiler: compiler_path}

                executor = type('Executor', (cls,), {'runtime_dict': result})
                success = executor.run_self_test(output=False)
                if success:
                    # Don't pollute the YAML in the usual case where it's -client
                    if mode == 'client':
                        del result[cls_vm_mode]
                    return result, success, 'Using %s (%s VM)' % (vm_path, mode)
            else:
                # If absolutely no VM mode works, then we've failed the self test
                return result, False, 'Failed self-test'
        else:
            return {}, False, 'Invalid JDK'
<commit_msg>Fix untyped mypy errors from java_executor.py<commit_after>import abc
import errno
import os
import re
import subprocess
import sys
from subprocess import Popen
from typing import Optional

from dmoj.error import CompileError, InternalError
from dmoj.executors.compiled_executor import CompiledExecutor
from dmoj.result import Result
from dmoj.utils.unicode import utf8bytes, utf8text

recomment = re.compile(r'/\*.*?\*/', re.DOTALL | re.U)
restring = re.compile(r''''(?:\\.|[^'\\])'|"(?:\\.|[^"\\])*"''', re.DOTALL | re.U)
reinline_comment = re.compile(r'//.*?(?=[\r\n])', re.U)
reclass = re.compile(r'\bpublic\s+(?:strictfp\s+)?(?:(?:abstract|final)\s+)?(?:strictfp\s+)?class\s+([\w\$][\w\$]*?)\b',
                     re.U)
repackage = re.compile(r'\bpackage\s+([^.;]+(?:\.[^.;]+)*?);', re.U)

JAVA_SANDBOX = os.path.abspath(os.path.join(os.path.dirname(__file__), 'java_sandbox.jar'))

POLICY_PREFIX = '''\
grant codeBase "file:///{agent}" {{
    permission java.io.FilePermission "state", "write";
}};

'''

with open(os.path.join(os.path.dirname(__file__), 'java-security.policy')) as policy_file:
    policy = policy_file.read()

def find_class(source):
    source = reinline_comment.sub('', restring.sub('', recomment.sub('', source)))
    class_name = reclass.search(source)
    if class_name is None:
        raise CompileError('No public class: your main class must be declared as a "public class"\n')
    package = repackage.search(source)
    if package:
        raise CompileError('Invalid package %s: do not declare package\n' % package.group(1))
    return class_name

class JavaExecutor(CompiledExecutor):
    ext = 'java'

    nproc = -1
    fsize = 64  # Allow 64 bytes for dumping state file.
    address_grace = 786432

    jvm_regex: Optional[str] = None
    security_policy = policy

    def __init__(self, problem_id, source_code, **kwargs):
        self._class_name = None
        super(JavaExecutor, self).__init__(problem_id, source_code, **kwargs)

    @property
    @abc.abstractmethod
    def compiler(self) -> str:
        pass

    @property
    @abc.abstractmethod
    def vm(self) -> str:
        pass

    def create_files(self, problem_id, source_code, *args, **kwargs):
        super(JavaExecutor, self).create_files(problem_id, source_code, *args, **kwargs)

        self._agent_file = JAVA_SANDBOX
        self._policy_file = self._file('security.policy')
        with open(self._policy_file, 'w') as file:
            file.write(POLICY_PREFIX.format(agent=self._agent_file) + self.security_policy)

    def get_compile_popen_kwargs(self):
        return {'executable': self.get_compiler()}

    def get_compiled_file(self):
        return None

    def get_security(self, launch_kwargs=None):
        return None

    def get_executable(self):
        return self.get_vm()

    def get_cmdline(self):
        agent_flags = '-javaagent:%s=policy:%s' % (self._agent_file, self._policy_file)
        for hint in self._hints:
            agent_flags += ',%s' % hint
        if self.unbuffered:
            agent_flags += ',nobuf'
        # 128m is equivalent to 1<<27 in Thread constructor
        return ['java', self.get_vm_mode(), agent_flags, '-Xss128m', '-Xmx%dK' % self.__memory_limit,
                '-XX:+UseSerialGC', '-XX:ErrorFile=submission_jvm_crash.log', self._class_name]

    def launch(self, *args, **kwargs):
        self.__memory_limit = kwargs['memory']
        kwargs['memory'] = 0
        return super(JavaExecutor, self).launch(*args, **kwargs)

    def launch_unsafe(self, *args, **kwargs):
        return Popen(['java', self.get_vm_mode(), self._class_name] + list(args),
                     executable=self.get_vm(), cwd=self._dir, **kwargs)

    def get_feedback(self, stderr, result, process):
        if process.returncode:
            try:
                with open(os.path.join(self._dir, 'submission_jvm_crash.log'), 'r') as err:
                    raise InternalError('\n\n' + err.read())
            except IOError:
                pass

        if not result.result_flag & Result.IR:
            return ''

        if b'Error: Main method not found in class' in stderr:
            exception = "public static void main(String[] args) not found"
        else:
            try:
                with open(os.path.join(self._dir, 'state'), 'r') as state:
                    exception = state.read().strip()
            except IOError:
                exception = "abnormal termination"  # Probably exited without calling shutdown hooks

        return exception

    @classmethod
    def get_vm(cls):
        return cls.runtime_dict.get(cls.vm)

    @classmethod
    def get_vm_mode(cls):
        return '-%s' % cls.runtime_dict.get(cls.vm + '_mode', 'client')

    @classmethod
    def get_compiler(cls):
        return cls.runtime_dict.get(cls.compiler)

    @classmethod
    def initialize(cls, sandbox=True):
        if cls.get_vm() is None or cls.get_compiler() is None:
            return False
        if not os.path.isfile(cls.get_vm()) or not os.path.isfile(cls.get_compiler()):
            return False
        return cls.run_self_test(sandbox)

    @classmethod
    def test_jvm(cls, name, path):
        raise NotImplementedError()

    @classmethod
    def get_versionable_commands(cls):
        return [('javac', cls.get_compiler())]

    @classmethod
    def get_version_flags(cls, command):
        return ['-version']

    @classmethod
    def autoconfig(cls):
        if cls.jvm_regex is None:
            return {}, False, 'Unimplemented'

        JVM_DIR = '/usr/local' if sys.platform.startswith('freebsd') else '/usr/lib/jvm'
        regex = re.compile(cls.jvm_regex)

        try:
            vms = os.listdir(JVM_DIR)
        except OSError:
            vms = []

        for item in vms:
            path = os.path.join(JVM_DIR, item)
            if not os.path.isdir(path) or os.path.islink(path):
                continue

            if regex.match(item):
                try:
                    config, success, message = cls.test_jvm(item, path)
                except (NotImplementedError, TypeError, ValueError):
                    return {}, False, 'Unimplemented'
                else:
                    if success:
                        return config, success, message
        return {}, False, 'Could not find JVM'

    @classmethod
    def unravel_java(cls, path):
        with open(path, 'rb') as f:
            if f.read(2) != '#!':
                return path

        with open(os.devnull, 'w') as devnull:
            process = subprocess.Popen(['bash', '-x', path, '-version'], stdout=devnull, stderr=subprocess.PIPE)

        log = [i for i in process.communicate()[1].split('\n') if 'exec' in i]
        cmdline = log[-1].lstrip('+ ').split()
        return cmdline[1] if len(cmdline) > 1 else path

class JavacExecutor(JavaExecutor):
    def create_files(self, problem_id, source_code, *args, **kwargs):
        super(JavacExecutor, self).create_files(problem_id, source_code, *args, **kwargs)
        # This step is necessary because of Unicode classnames
        try:
            source_code = utf8text(source_code)
        except UnicodeDecodeError:
            raise CompileError('Your UTF-8 is bad, and you should feel bad')
        class_name = find_class(source_code)
        self._code = self._file('%s.java' % class_name.group(1))
        try:
            with open(self._code, 'wb') as fo:
                fo.write(utf8bytes(source_code))
        except IOError as e:
            if e.errno in (errno.ENAMETOOLONG, errno.ENOENT, errno.EINVAL):
                raise CompileError('Why do you need a class name so long? '
                                   'As a judge, I sentence your code to death.\n')
            raise
        self._class_name = class_name.group(1)

    def get_compile_args(self):
        return [self.get_compiler(), '-Xlint', '-encoding', 'UTF-8', self._code]

    def handle_compile_error(self, output):
        if b'is public, should be declared in a file named' in utf8bytes(output):
            raise CompileError('You are a troll. Trolls are not welcome. '
                               'As a judge, I sentence your code to death.\n')
        raise CompileError(output)

    @classmethod
    def test_jvm(cls, name, path):
        vm_path = os.path.join(path, 'bin', 'java')
        compiler_path = os.path.join(path, 'bin', 'javac')

        if os.path.isfile(vm_path) and os.path.isfile(compiler_path):
            # Not all JVMs have the same VMs available; specifically,
            # OpenJDK for ARM has no client VM, but has dcevm+server. So, we test
            # a bunch and if it's not the default (-client), then we list it
            # in the config.
            vm_modes = ['client', 'server', 'dcevm', 'zero']
            cls_vm_mode = cls.vm + '_mode'
            for mode in vm_modes:
                result = {cls.vm: vm_path, cls_vm_mode: mode, cls.compiler: compiler_path}

                executor = type('Executor', (cls,), {'runtime_dict': result})
                success = executor.run_self_test(output=False)
                if success:
                    # Don't pollute the YAML in the usual case where it's -client
                    if mode == 'client':
                        del result[cls_vm_mode]
                    return result, success, 'Using %s (%s VM)' % (vm_path, mode)
            else:
                # If absolutely no VM mode works, then we've failed the self test
                return result, False, 'Failed self-test'
        else:
            return {}, False, 'Invalid JDK'
<|endoftext|>