import mock
import pytest
from os.path import abspath, dirname, join
import sys

from praw.models import (Button, ButtonWidget, Calendar, CommunityList,
                         CustomWidget, Menu, MenuLink, IDCard, Image,
                         ImageData, ImageWidget, ModeratorsWidget,
                         PostFlairWidget, Redditor, RulesWidget, Submenu,
                         Subreddit, TextArea, Widget)
from ... import IntegrationTest

if sys.version_info.major > 2:
    basestring = str  # pylint: disable=invalid-name

class TestButtonWidget(IntegrationTest):
    @staticmethod
    def image_path(name):
        test_dir = abspath(dirname(sys.modules[__name__].__file__))
        return join(test_dir, '..', '..', 'files', name)

    def test_button_widget(self):
        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets
        with self.recorder.use_cassette('TestSubredditWidgets.fetch_widgets'):
            button_widget = None
            for widget in widgets.sidebar:
                if isinstance(widget, ButtonWidget):
                    button_widget = widget
                    break
            assert isinstance(button_widget, ButtonWidget)
            assert len(button_widget) >= 1
            assert all(isinstance(button, Button) for button in
                       button_widget.buttons)
            assert button_widget == button_widget
            assert button_widget.id == button_widget
            assert button_widget in widgets.sidebar

            assert button_widget[0].text
            assert button_widget.shortName
            assert hasattr(button_widget, 'description')

            assert subreddit == button_widget.subreddit

    @mock.patch('time.sleep', return_value=None)
    def test_create_and_update_and_delete(self, _):
        self.reddit.read_only = False

        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets

        with self.recorder.use_cassette(
                'TestButtonWidget.test_create_and_update_and_delete'):
            styles = {'headerColor': '#123456', 'backgroundColor': '#bb0e00'}
            my_image = widgets.mod.upload_image(self.image_path('test.png'))
            buttons = [
                {
                    'kind': 'text',
                    'text': 'View source',
                    'url': 'https://github.com/praw-dev/praw',
                    'color': '#FF0000',
                    'textColor': '#00FF00',
                    'fillColor': '#0000FF',
                    'hoverState': {
                        'kind': 'text',
                        'text': 'VIEW SOURCE!',
                        'color': '#FFFFFF',
                        'textColor': '#000000',
                        'fillColor': '#0000FF'
                    }
                },
                {
                    'kind': 'image',
                    'text': 'View documentation',
                    'linkUrl': 'https://praw.readthedocs.io',
                    'url': my_image,
                    'height': 200,
                    'width': 200,
                    'hoverState': {
                        'kind': 'image',
                        'url': my_image,
                        'height': 200,
                        'width': 200
                    }
                },
                {
                    'kind': 'text',
                    'text': '/r/redditdev',
                    'url': 'https://reddit.com/r/redditdev',
                    'color': '#000000',
                    'textColor': '#FF00FF',
                    'fillColor': '#005500'
                }
            ]
            widget = widgets.mod.add_button_widget(
                'Things to click', 'Click some of these *cool* links!',
                buttons, styles)

            assert isinstance(widget, ButtonWidget)
            assert len(widget) == 3
            assert all(isinstance(item, Button) for item in widget)
            assert widget.shortName == 'Things to click'
            assert widget.description == 'Click some of these *cool* links!'
            assert widget.styles == styles

            assert widget[0].text == 'View source'
            assert widget[0].url == 'https://github.com/praw-dev/praw'
            assert widget[2].text == '/r/redditdev'
            assert widget[2].url == 'https://reddit.com/r/redditdev'

            assert widget[1].text == 'View documentation'
            assert widget[1].linkUrl == 'https://praw.readthedocs.io'
            assert widget[1].hoverState['kind'] == 'image'
            assert widget[1].hoverState['height'] == 200

            widgets.refresh()  # the links are initially invalid
            for new_widget in widgets.sidebar:
                if new_widget == widget:
                    widget = new_widget
                    break

            widget = widget.mod.update(shortName='New short name')

            assert isinstance(widget, ButtonWidget)
            assert len(widget) == 3
            assert all(isinstance(item, Button) for item in widget)
            assert widget.shortName == 'New short name'
            assert widget.description == 'Click some of these *cool* links!'
            assert widget.styles == styles

            assert widget[0].text == 'View source'
            assert widget[0].url == 'https://github.com/praw-dev/praw'
            assert widget[2].text == '/r/redditdev'
            assert widget[2].url == 'https://reddit.com/r/redditdev'

            assert widget[1].text == 'View documentation'
            assert widget[1].linkUrl == 'https://praw.readthedocs.io'
            assert widget[1].hoverState['kind'] == 'image'
            assert widget[1].hoverState['height'] == 200

            buttons.reverse()
            widget = widget.mod.update(buttons=buttons)

            assert isinstance(widget, ButtonWidget)
            assert len(widget) == 3
            assert all(isinstance(item, Button) for item in widget)
            assert widget.shortName == 'New short name'
            assert widget.description == 'Click some of these *cool* links!'
            assert widget.styles == styles

            assert widget[0].text == '/r/redditdev'
            assert widget[0].url == 'https://reddit.com/r/redditdev'
            assert widget[2].text == 'View source'
            assert widget[2].url == 'https://github.com/praw-dev/praw'

            assert widget[1].text == 'View documentation'
            assert widget[1].linkUrl == 'https://praw.readthedocs.io'
            assert widget[1].hoverState['kind'] == 'image'
            assert widget[1].hoverState['height'] == 200

            widget.mod.delete()

class TestCalendar(IntegrationTest):

    def test_calendar(self):
        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets
        with self.recorder.use_cassette('TestSubredditWidgets.fetch_widgets'):
            calendar = None
            for widget in widgets.sidebar:
                if isinstance(widget, Calendar):
                    calendar = widget
                    break
            assert isinstance(calendar, Calendar)
            assert calendar == calendar
            assert calendar.id == calendar
            assert calendar in widgets.sidebar

            assert isinstance(calendar.configuration, dict)
            assert hasattr(calendar, 'requiresSync')

            assert subreddit == calendar.subreddit

    @mock.patch('time.sleep', return_value=None)
    def test_create_and_update_and_delete(self, _):
        self.reddit.read_only = False

        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets

        with self.recorder.use_cassette(
                'TestCalendar.test_create_and_update_and_delete'):
            styles = {'headerColor': '#123456', 'backgroundColor': '#bb0e00'}
            config = {'numEvents': 10,
                      'showDate': True,
                      'showDescription': False,
                      'showLocation': False,
                      'showTime': True,
                      'showTitle': True}
            cal_id = 'ccahu0rstno2jrvioq4ccffn78@group.calendar.google.com'
            widget = widgets.mod.add_calendar('Upcoming Events', cal_id, True,
                                              config, styles)

            assert isinstance(widget, Calendar)
            assert widget.shortName == 'Upcoming Events'
            assert widget.googleCalendarId == 'ccahu0rstno2jrvioq4ccffn78@' \
                                              'group.calendar.google.com'
            assert widget.configuration == config
            assert widget.styles == styles

            widget = widget.mod.update(shortName='Past Events :(')

            assert isinstance(widget, Calendar)
            assert widget.shortName == 'Past Events :('
            assert widget.googleCalendarId == 'ccahu0rstno2jrvioq4ccffn78@' \
                                              'group.calendar.google.com'
            assert widget.configuration == config
            assert widget.styles == styles

            widget.mod.delete()

class TestCommunityList(IntegrationTest):

    def test_community_list(self):
        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets
        with self.recorder.use_cassette('TestSubredditWidgets.fetch_widgets'):
            comm_list = None
            for widget in widgets.sidebar:
                if isinstance(widget, CommunityList):
                    comm_list = widget
                    break
            assert isinstance(comm_list, CommunityList)
            assert len(comm_list) >= 1
            assert all(isinstance(subreddit, Subreddit) for subreddit in
                       comm_list)
            assert comm_list == comm_list
            assert comm_list.id == comm_list
            assert comm_list in widgets.sidebar

            assert comm_list.shortName
            assert comm_list[0] in comm_list

            assert subreddit == comm_list.subreddit

    @mock.patch('time.sleep', return_value=None)
    def test_create_and_update_and_delete(self, _):
        self.reddit.read_only = False

        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets

        with self.recorder.use_cassette(
                'TestCommunityList.test_create_and_update_and_delete'):
            styles = {'headerColor': '#123456', 'backgroundColor': '#bb0e00'}
            subreddits = ['learnpython', self.reddit.subreddit('redditdev')]
            widget = widgets.mod.add_community_list('My fav subs', subreddits,
                                                    styles)

            assert isinstance(widget, CommunityList)
            assert widget.shortName == 'My fav subs'
            assert widget.styles == styles
            assert self.reddit.subreddit('learnpython') in widget
            assert 'redditdev' in widget

            widget = widget.mod.update(shortName='My least fav subs :(',
                                       data=['redesign'])

            assert isinstance(widget, CommunityList)
            assert widget.shortName == 'My least fav subs :('
            assert widget.styles == styles
            assert self.reddit.subreddit('redesign') in widget

            widget.mod.delete()

class TestCustomWidget(IntegrationTest):
    @staticmethod
    def image_path(name):
        test_dir = abspath(dirname(sys.modules[__name__].__file__))
        return join(test_dir, '..', '..', 'files', name)

    @mock.patch('time.sleep', return_value=None)
    def test_create_and_update_and_delete(self, _):
        self.reddit.read_only = False

        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets

        with self.recorder.use_cassette(
                'TestCustomWidget.test_create_and_update_and_delete'):
            image_dicts = [{'width': 0,
                            'height': 0,
                            'name': 'a',
                            'url': widgets.mod.upload_image(self.image_path(
                                'test.png'))}]

            styles = {'headerColor': '#123456', 'backgroundColor': '#bb0e00'}
            widget = widgets.mod.add_custom_widget('My widget',
                                                   '# Hello world!', '/**/',
                                                   200, image_dicts, styles)

            assert isinstance(widget, CustomWidget)
            assert widget.shortName == 'My widget'
            assert widget.text == '# Hello world!'
            assert widget.css == '/**/'
            assert widget.height == 200
            assert widget.styles == styles
            assert len(widget.imageData) == 1
            assert all(isinstance(img, ImageData) for img in widget.imageData)

            # initially, image URLs are incorrect, so we much refresh to get
            # the proper ones.
            widgets.refresh()
            refreshed = widgets.sidebar[-1]
            assert refreshed == widget
            widget = refreshed

            new_css = 'h1,h2,h3,h4,h5,h6 {color: #00ff00;}'
            widget = widget.mod.update(css=new_css)

            assert isinstance(widget, CustomWidget)
            assert widget.shortName == 'My widget'
            assert widget.text == '# Hello world!'
            assert widget.css == new_css
            assert widget.height == 200
            assert widget.styles == styles
            assert len(widget.imageData) == 1
            assert all(isinstance(img, ImageData) for img in widget.imageData)

            widget.mod.delete()

    def test_custom_widget(self):
        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets
        with self.recorder.use_cassette('TestSubredditWidgets.fetch_widgets'):
            custom = None
            for widget in widgets.sidebar:
                if isinstance(widget, CustomWidget):
                    custom = widget
                    break
            assert isinstance(custom, CustomWidget)
            assert len(custom.imageData) > 0
            assert all(isinstance(img_data, ImageData) for img_data in
                       custom.imageData)
            assert custom == custom
            assert custom.id == custom
            assert custom in widgets.sidebar

            assert 500 >= custom.height >= 50
            assert custom.text
            assert custom.css
            assert custom.shortName

            assert subreddit == custom.subreddit

class TestIDCard(IntegrationTest):

    def test_id_card(self):
        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets
        with self.recorder.use_cassette('TestSubredditWidgets.fetch_widgets'):
            card = widgets.id_card
            assert isinstance(card, IDCard)
            assert card == card
            assert card.id == card

            assert card.shortName
            assert card.currentlyViewingText
            assert card.subscribersText

            assert subreddit == card.subreddit

class TestImageWidget(IntegrationTest):
    @staticmethod
    def image_path(name):
        test_dir = abspath(dirname(sys.modules[__name__].__file__))
        return join(test_dir, '..', '..', 'files', name)

    @mock.patch('time.sleep', return_value=None)
    def test_create_and_update_and_delete(self, _):
        self.reddit.read_only = False

        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets

        with self.recorder.use_cassette(
                'TestImageWidget.test_create_and_update_and_delete'):
            image_paths = (self.image_path(name) for name in
                           ('test.jpg', 'test.png'))
            image_dicts = [{'width': 0, 'height': 0, 'linkUrl': '',
                            'url': widgets.mod.upload_image(img_path)}
                           for img_path in image_paths]

            styles = {'headerColor': '#123456', 'backgroundColor': '#bb0e00'}
            widget = widgets.mod.add_image_widget(short_name='My new pics!',
                                                  data=image_dicts,
                                                  styles=styles)

            assert isinstance(widget, ImageWidget)
            assert widget.shortName == 'My new pics!'
            assert widget.styles == styles
            assert len(widget) == 2
            assert all(isinstance(img, Image) for img in widget)

            widget = widget.mod.update(shortName='My old pics :(',
                                       data=image_dicts[:1])

            assert isinstance(widget, ImageWidget)
            assert widget.shortName == 'My old pics :('
            assert widget.styles == styles
            assert len(widget) == 1
            assert all(isinstance(img, Image) for img in widget)

            widget.mod.delete()

    def test_image_widget(self):
        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets
        with self.recorder.use_cassette('TestSubredditWidgets.fetch_widgets'):
            img_widget = None
            for widget in widgets.sidebar:
                if isinstance(widget, ImageWidget):
                    img_widget = widget
                    break
            assert isinstance(img_widget, ImageWidget)
            assert len(img_widget) >= 1
            assert all(isinstance(image, Image) for image in img_widget)
            assert img_widget == img_widget
            assert img_widget.id == img_widget
            assert img_widget in widgets.sidebar

            assert img_widget[0].linkUrl
            assert img_widget.shortName

            assert subreddit == img_widget.subreddit

class TestMenu(IntegrationTest):

    @mock.patch('time.sleep', return_value=None)
    def test_create_and_update_and_delete(self, _):
        self.reddit.read_only = False

        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets

        menu_contents = [
            {'text': 'My homepage', 'url': 'https://example.com'},
            {'text': 'Python packages',
             'children': [
                 {'text': 'PRAW', 'url': 'https://praw.readthedocs.io/'},
                 {'text': 'requests', 'url': 'http://python-requests.org'}
             ]},
            {'text': 'Reddit homepage', 'url': 'https://reddit.com'}
        ]

        with self.recorder.use_cassette(
                'TestMenu.test_create_and_update_and_delete'):
            widget = widgets.mod.add_menu(menu_contents)

            assert isinstance(widget, Menu)
            assert len(widget) == 3
            assert all(isinstance(item, (Submenu, MenuLink))
                       for item in widget)
            assert all(all(isinstance(item, MenuLink) for item in subm)
                       for subm in widget if isinstance(subm, Submenu))

            assert widget[0].text == 'My homepage'
            assert widget[0].url == 'https://example.com'
            assert widget[2].text == 'Reddit homepage'
            assert widget[2].url == 'https://reddit.com'

            assert widget[1].text == 'Python packages'
            assert widget[1][0].text == 'PRAW'
            assert widget[1][0].url == 'https://praw.readthedocs.io/'
            assert widget[1][1].text == 'requests'
            assert widget[1][1].url == 'http://python-requests.org'

            menu_contents.reverse()
            widget = widget.mod.update(data=menu_contents)

            assert isinstance(widget, Menu)
            assert len(widget) == 3
            assert all(isinstance(item, (Submenu, MenuLink))
                       for item in widget)
            assert all(all(isinstance(item, MenuLink) for item in subm)
                       for subm in widget if isinstance(subm, Submenu))

            assert widget[0].text == 'Reddit homepage'
            assert widget[0].url == 'https://reddit.com'
            assert widget[2].text == 'My homepage'
            assert widget[2].url == 'https://example.com'

            assert widget[1].text == 'Python packages'
            assert widget[1][0].text == 'PRAW'
            assert widget[1][0].url == 'https://praw.readthedocs.io/'
            assert widget[1][1].text == 'requests'
            assert widget[1][1].url == 'http://python-requests.org'

            widget.mod.delete()

    def test_menu(self):
        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets
        with self.recorder.use_cassette('TestSubredditWidgets.fetch_widgets'):
            menu = None
            for widget in widgets.topbar:
                if isinstance(widget, Menu):
                    menu = widget
                    break
            assert isinstance(menu, Menu)
            assert all(isinstance(item, (MenuLink, Submenu)) for item in menu)
            assert menu == menu
            assert menu.id == menu
            assert menu in widgets.topbar
            assert len(menu) >= 1
            assert menu[0].text

            assert subreddit == menu.subreddit

            submenu = None
            for child in menu:
                if isinstance(child, Submenu):
                    submenu = child
                    break
            assert isinstance(submenu, Submenu)
            assert len(submenu) >= 0
            assert all(isinstance(child, MenuLink) for child in submenu)
            assert submenu[0].text
            assert submenu[0].url

class TestModeratorsWidget(IntegrationTest):

    def test_moderators_widget(self):
        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets
        with self.recorder.use_cassette('TestSubredditWidgets.fetch_widgets'):
            mods = widgets.moderators_widget
            assert isinstance(mods, ModeratorsWidget)
            assert all(isinstance(mod, Redditor) for mod in mods)
            assert mods == mods
            assert mods.id == mods

            assert len(mods) >= 1
            assert isinstance(mods[0], Redditor)

            assert subreddit == mods.subreddit

class TestPostFlairWidget(IntegrationTest):

    @mock.patch('time.sleep', return_value=None)
    def test_create_and_update_and_delete(self, _):
        self.reddit.read_only = False

        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets

        with self.recorder.use_cassette(
                'TestPostFlairWidget.test_create_and_update_and_delete'):
            flairs = [f['id'] for f in subreddit.flair.link_templates]

            styles = {'headerColor': '#123456', 'backgroundColor': '#bb0e00'}
            widget = widgets.mod.add_post_flair_widget('Some flairs', 'list',
                                                       flairs, styles)

            assert isinstance(widget, PostFlairWidget)
            assert widget.shortName == 'Some flairs'
            assert widget.display == 'list'
            assert widget.order == flairs
            assert widget.styles == styles
            assert len(widget) == 2
            assert all(flair_id in widget.templates for flair_id in widget)

            widget = widget.mod.update(display='cloud')

            assert isinstance(widget, PostFlairWidget)
            assert widget.shortName == 'Some flairs'
            assert widget.display == 'cloud'
            assert widget.order == flairs
            assert widget.styles == styles
            assert len(widget) == 2
            assert all(flair_id in widget.templates for flair_id in widget)

            widget = widget.mod.update(order=widget.order[1:])

            assert isinstance(widget, PostFlairWidget)
            assert widget.shortName == 'Some flairs'
            assert widget.display == 'cloud'
            assert widget.order == flairs[1:]
            assert widget.styles == styles
            assert len(widget) == 1
            assert all(flair_id in widget.templates for flair_id in widget)

            widget.mod.delete()

    def test_post_flair_widget(self):
        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets
        with self.recorder.use_cassette('TestSubredditWidgets.fetch_widgets'):
            pf_widget = None
            for widget in widgets.sidebar:
                if isinstance(widget, PostFlairWidget):
                    pf_widget = widget
                    break
            assert isinstance(pf_widget, PostFlairWidget)
            assert len(pf_widget) >= 1
            assert all(flair_id in widget.templates for flair_id in widget)
            assert pf_widget == pf_widget
            assert pf_widget.id == pf_widget
            assert pf_widget in widgets.sidebar

            assert pf_widget.shortName
            assert all(flair in pf_widget for flair in pf_widget)

            assert subreddit == pf_widget.subreddit

class TestRulesWidget(IntegrationTest):

    def test_rules_widget(self):

        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets
        with self.recorder.use_cassette('TestSubredditWidgets.fetch_widgets'):
            rules = None
            for widget in widgets.sidebar:
                if isinstance(widget, RulesWidget):
                    rules = widget
                    break
            assert isinstance(rules, RulesWidget)
            assert rules == rules
            assert rules.id == rules

            assert rules.display

            assert len(rules) > 0
            assert subreddit == rules.subreddit

class TestSubredditWidgets(IntegrationTest):
    def test_bad_attribute(self):
        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets
        with self.recorder.use_cassette('TestSubredditWidgets.fetch_widgets'):
            with pytest.raises(AttributeError):
                widgets.nonexistant_attribute

    def test_items(self):
        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets
        with self.recorder.use_cassette('TestSubredditWidgets.fetch_widgets'):
            assert isinstance(widgets.items, dict)

    def test_progressive_images(self):
        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets

        def has_progressive(widgets_):
            # best way I could figure if an image is progressive
            sign = 'fm=pjpg'

            for widget in widgets_.sidebar:
                if isinstance(widget, ImageWidget):
                    for image in widget:
                        if sign in image.url:
                            return True
                elif isinstance(widget, CustomWidget):
                    for image_data in widget.imageData:
                        if sign in image_data.url:
                            return True

            return False

        with self.recorder.use_cassette(
                'TestSubredditWidgets.test_progressive_images'):
            widgets.progressive_images = True
            assert has_progressive(widgets)
            widgets.progressive_images = False
            widgets.refresh()
            assert not has_progressive(widgets)
            widgets.progressive_images = True
            widgets.refresh()
            assert has_progressive(widgets)

    def test_refresh(self):
        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets
        with self.recorder.use_cassette('TestSubredditWidgets.test_refresh'):
            assert widgets.sidebar  # to fetch
            old_sidebar = widgets.sidebar  # reference, not value
            widgets.refresh()
            assert old_sidebar is not widgets.sidebar  # should be new list

    def test_repr(self):
        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets
        assert ("SubredditWidgets(subreddit=Subreddit(display_name='"
                "{}'))").format(pytest.placeholders.test_subreddit) == repr(
            widgets)

    def test_sidebar(self):
        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets
        with self.recorder.use_cassette('TestSubredditWidgets.fetch_widgets'):
            assert len(widgets.sidebar) >= 1  # also tests lazy-loading

        # all items should be Widget subclasses
        assert all(isinstance(widget, Widget) and type(widget) != Widget
                   for widget in widgets.sidebar)

    def test_specials(self):
        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets
        with self.recorder.use_cassette('TestSubredditWidgets.fetch_widgets'):
            assert isinstance(widgets.id_card, IDCard)
            assert isinstance(widgets.moderators_widget, ModeratorsWidget)

    def test_topbar(self):
        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets
        with self.recorder.use_cassette('TestSubredditWidgets.fetch_widgets'):
            assert 1 <= len(widgets.topbar)
            assert all(isinstance(widget, Widget) and type(widget) != Widget
                       for widget in widgets.topbar)

class TestSubredditWidgetsModeration(IntegrationTest):
    @staticmethod
    def image_path(name):
        test_dir = abspath(dirname(sys.modules[__name__].__file__))
        return join(test_dir, '..', '..', 'files', name)

    @mock.patch('time.sleep', return_value=None)
    def test_reorder(self, _):
        self.reddit.read_only = False
        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets

        with self.recorder.use_cassette(
                'TestSubredditWidgetsModeration.test_reorder'):
            old_order = list(widgets.sidebar)
            new_order = list(reversed(old_order))

            widgets.mod.reorder(new_order)
            widgets.refresh()
            assert list(widgets.sidebar) == new_order

            widgets.mod.reorder(old_order)
            widgets.refresh()
            assert list(widgets.sidebar) == old_order

            mixed_types = [thing if i % 2 == 0 else thing.id
                           for i, thing in enumerate(new_order)]
            # mixed_types has some str and some Widget.
            assert any(isinstance(thing, basestring) for thing in mixed_types)
            assert any(isinstance(thing, Widget) for thing in mixed_types)

            widgets.mod.reorder(mixed_types)
            widgets.refresh()
            assert list(widgets.sidebar) == new_order

    @mock.patch('time.sleep', return_value=None)
    def test_upload_image(self, _):
        self.reddit.read_only = False
        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets

        with self.recorder.use_cassette(
                'TestSubredditWidgetsModeration.test_upload_image'):
            for image in ('test.jpg', 'test.png'):
                image_url = widgets.mod.upload_image(self.image_path(image))
                assert image_url

class TestTextArea(IntegrationTest):
    @mock.patch('time.sleep', return_value=None)
    def test_create_and_update_and_delete(self, _):
        self.reddit.read_only = False

        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets

        with self.recorder.use_cassette(
                'TestTextArea.test_create_and_update_and_delete'):
            styles = {'headerColor': '#123456', 'backgroundColor': '#bb0e00'}
            widget = widgets.mod.add_text_area(short_name='My new widget!',
                                               text='Hello world!',
                                               styles=styles)

            assert isinstance(widget, TextArea)
            assert widget.shortName == 'My new widget!'
            assert widget.styles == styles
            assert widget.text == 'Hello world!'

            widget = widget.mod.update(shortName='My old widget :(',
                                       text='Feed me')

            assert isinstance(widget, TextArea)
            assert widget.shortName == 'My old widget :('
            assert widget.styles == styles
            assert widget.text == 'Feed me'

            widget.mod.delete()

    def test_text_area(self):
        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets
        with self.recorder.use_cassette('TestSubredditWidgets.fetch_widgets'):
            text = None
            for widget in widgets.sidebar:
                if isinstance(widget, TextArea):
                    text = widget
                    break
            assert isinstance(text, TextArea)
            assert text == text
            assert text.id == text
            assert text in widgets.sidebar
            assert text in widgets.sidebar

            assert text.shortName
            assert text.text

            assert subreddit == text.subreddit

class TestWidget(IntegrationTest):
    def test_inequality(self):
        subreddit = self.reddit.subreddit(pytest.placeholders.test_subreddit)
        widgets = subreddit.widgets
        with self.recorder.use_cassette('TestSubredditWidgets.fetch_widgets'):
            assert len(widgets.sidebar) >= 2
        assert widgets.sidebar[0] != widgets.sidebar[1]
        assert widgets.sidebar[0] != widgets.sidebar[1].id
        assert u'\xf0\x9f\x98\x80' != widgets.sidebar[0]  # for python 2


#!/usr/bin/env python

#
# Script inspired in bud:
#   https://github.com/indutny/bud
#

import platform
import os
import subprocess
import sys

CC = os.environ.get('CC', 'cc')
script_dir = os.path.dirname(__file__)
root = os.path.normpath(os.path.join(script_dir, '..'))
output_dir = os.path.join(os.path.abspath(root), 'out')

sys.path.insert(0, os.path.join(root, 'deps', 'gyp', 'pylib'))
try:
    import gyp
except ImportError:
    print('Error: you need to install gyp in deps/gyp first, run:')
    print('  ./scripts/get-dep.sh gyp')
    sys.exit(42)

def host_arch():
    machine = platform.machine()
    if machine == 'i386': return 'ia32'
    if machine == 'x86_64': return 'x64'
    if machine == 'aarch64': return 'arm64'
    if machine == 'mips64': return 'mips64el'
    if machine.startswith('arm'): return 'arm'
    if machine.startswith('mips'): return 'mips'
    return machine  # Return as-is and hope for the best.

def compiler_version():
    proc = subprocess.Popen(CC.split() + ['--version'], stdout=subprocess.PIPE)
    is_clang = b'clang' in proc.communicate()[0].split(b'\n')[0]
    proc = subprocess.Popen(CC.split() + ['-dumpversion'], stdout=subprocess.PIPE)
    version = proc.communicate()[0].split(b'.')
    mayor_version = int(version[:1][0])
    if is_clang is False and mayor_version >= 7:
        proc = subprocess.Popen(CC.split() + ['-dumpfullversion'], stdout=subprocess.PIPE)
        version = proc.communicate()[0].split(b'.')
    version = map(int, version[:2])
    version = tuple(version)
    return (version, is_clang)

def run_gyp(args):
    rc = gyp.main(args)
    if rc != 0:
        print('Error running GYP')
        sys.exit(rc)

if __name__ == '__main__':
    args = sys.argv[1:]

    # GYP bug.
    # On msvs it will crash if it gets an absolute path.
    # On Mac/make it will crash if it doesn't get an absolute path.
    # NOTE ibc: Not sure that it requires absolute path in Mac/make...
    if sys.platform == 'win32':
        args.append(os.path.join(root, 'mediasoup-worker.gyp'))
        common_fn  = os.path.join(root, 'common.gypi')
        # we force vs 2010 over 2008 which would otherwise be the default for gyp.
        if not os.environ.get('GYP_MSVS_VERSION'):
            os.environ['GYP_MSVS_VERSION'] = '2010'
    else:
        args.append(os.path.join(os.path.abspath(root), 'mediasoup-worker.gyp'))
        common_fn  = os.path.join(os.path.abspath(root), 'common.gypi')

    if os.path.exists(common_fn):
        args.extend(['-I', common_fn])

    args.append('--depth=' + root)

    # There's a bug with windows which doesn't allow this feature.
    if sys.platform != 'win32':
        if '-f' not in args:
            args.extend('-f make'.split())
        if 'ninja' not in args:
            args.extend(['-Goutput_dir=' + output_dir])
            args.extend(['--generator-output', output_dir])
        (major, minor), is_clang = compiler_version()
        args.append('-Dgcc_version=%d' % (10 * major + minor))
        args.append('-Dclang=%d' % int(is_clang))
        if is_clang is False and major == 4 and minor <= 8:
            raise RuntimeError('gcc <= 4.8 not supported, please upgrade your gcc')

    if not any(a.startswith('-Dhost_arch=') for a in args):
        args.append('-Dhost_arch=%s' % host_arch())

    if not any(a.startswith('-Dtarget_arch=') for a in args):
        args.append('-Dtarget_arch=%s' % host_arch())

    if any(a.startswith('-Dopenssl_fips=') for a in args):
        fips_fn = os.path.join(os.path.abspath(root), 'fips.gypi')
        args.extend(['-I', fips_fn])
    else:
        args.append('-Dopenssl_fips=')

    if 'asan' in args:
        args.append('-Dmediasoup_asan=true')
        args = filter(lambda arg: arg != 'asan', args)
    else:
        args.append('-Dmediasoup_asan=false')

    args.append('-Dnode_byteorder=' + sys.byteorder)

    gyp_args = list(args)
    print(gyp_args)
    run_gyp(gyp_args)


'''
Created on 2020-08-11

@author: wf
'''
import unittest
import time
from lodstorage.sparql import SPARQL
from lodstorage.lod import LOD
from ptp.location import CountryManager, ProvinceManager, CityManager
import datetime
from collections import Counter
import getpass

class TestLocations(unittest.TestCase):
    '''
    check countries, provinces/states and cities
    '''
    def setUp(self):
        self.debug=False
        pass

    def tearDown(self):
        pass

    def testCityStorage(self):
        '''
        try storing city data in cache
        '''
        cim=CityManager(name="github")
        cim.fromLutangar()
        cim.store(cim.cityList)

    def testCities(self):
        '''
        test consolidating cities from different sources
        '''
        cim=CityManager('lutangarVersusOpenResearch')
        startTime=time.time()
        cim.fromLutangar()
        self.assertEqual(128769,(len(cim.cityList)))
        print ("reading %d cities from github took %5.1f secs" % (len(cim.cityList),time.time()-startTime))
        startTime=time.time()
        orCities=cim.fromOpenResearch(showProgress=True)
        cityCounter=Counter(orCities)
        uniqueCities=list(cityCounter.most_common())
        print ("reading %d cities from %d events from openresearch took %5.1f secs" % (len(uniqueCities),len(orCities),time.time()-startTime))
        print (cityCounter.most_common(1000))
        orCityList=[]
        for cityName,count in uniqueCities:
            orCityList.append({'name': cityName, 'count': count})
        startTime=time.time()
        validCities=LOD.intersect(cim.cityList, orCityList, 'name')
        print ("validating %d cities from openresearch took %5.1f secs" % (len(validCities),time.time()-startTime))

    def getDBPedia(self,mode='query',debug=False):
        endpoint="http://dbpedia.org/sparql"
        dbpedia=SPARQL(endpoint,mode=mode,debug=debug)
        return dbpedia

    def testDBPediaCities(self):
        '''
        https://github.com/LITMUS-Benchmark-Suite/dbpedia-graph-convertor/blob/master/get_data.py
        '''
        # kglf
        return
        dbpedia=self.getDBPedia()
        limit=100
        # Query to get the population of cities
        citiesWithPopulationQuery = """
            PREFIX dbo: <http://dbpedia.org/ontology/>
            PREFIX dbp: <http://dbpedia.org/property/>
            PREFIX dbr: <http://dbpedia.org/resource/>
            SELECT DISTINCT ?dbCity ?country ?name ?website ?population
            WHERE {
                ?dbCity a dbo:City .
                ?dbCity dbp:name ?name .
                ?dbCity dbo:country ?country .
                OPTIONAL { ?dbCity dbo:populationTotal ?population . }
                OPTIONAL { ?dbCity dbp:website ?website . }
            }
            LIMIT %d
            """ % limit
        cityList=dbpedia.queryAsListOfDicts(citiesWithPopulationQuery)
        cim=CityManager("dbpedia")
        LOD.setNone4List(cityList, ["population","website"])
        cim.store(cityList)

    def testDBPediaCountries(self):
        '''
        http://dbpedia.org/ontology/Country
        '''
        # kglf
        return
        dbpedia=self.getDBPedia()
        countriesQuery="""
        # https://opendata.stackexchange.com/a/7660/18245 - dbp:iso3166code not set ...
        PREFIX dbo: <http://dbpedia.org/ontology/>
SELECT ?country_name ?population ?isocode
WHERE {
  ?country_name a dbo:Country .
  ?country_name dbp:iso3166code ?isocode.
  OPTIONAL { ?country_name dbo:populationTotal ?population . }
}
        """
        countriesResult=dbpedia.query(countriesQuery)
        print(countriesResult)
        print(len(countriesResult))

    def getEndPoint(self):
        endpoint="https://query.wikidata.org/sparql"
        # check we have local wikidata copy:
        if getpass.getuser()=="travis":
            endpoint=None
        elif getpass.getuser()=="wf":
            # use 2018 wikidata copy
            #endpoint="http://blazegraph.bitplan.com/sparql"
            # use 2020 wikidata copy
            endpoint="http://jena.zeus.bitplan.com/wikidata"
        return endpoint

    def testWikiDataCities(self):
        '''
        test getting cities(human settlements to be precise)
        from Wikidata
        '''
        #endpoint=self.getEndPoint()
        # force caching - 3.5 hour query if done via endpoint!
        endpoint=None
        cm=CityManager("wikidata")
        cm.endpoint=endpoint
        cm.fromCache()
        print("found %d cities" % len(cm.cityList))
        self.assertTrue(len(cm.cityList)>=200000)

    def testWikiDataProvinces(self):
        '''
        test getting provinces from wikidata
        '''
        pm=ProvinceManager("wikidata")
        pm.endpoint=self.getEndPoint()
        pm.fromCache()
        print("found %d provinces" % len(pm.provinceList))
        self.assertTrue(len(pm.provinceList)>=195)

    def testWikiDataCountries(self):
        '''
        check local wikidata
        '''
        cm=CountryManager("wikidata")
        cm.endpoint=self.getEndPoint()
        cm.fromCache()
        self.assertTrue(len(cm.countryList)>=195)

        #    sparql=TestJena.getJena(debug=self.debug)
        #    errors=cm.storeToRDF(sparql)
        #    self.assertFalse(sparql.printErrors(errors))
        #    doimport=True
        #    if doimport:
        #        cm2=CountryManager()
        #        cm2.fromRDF(sparql)
        #    self.assertEqual(cm.countryList,cm2.countryList)

    def testCountryManager(self):
        '''
        test storying countries in SQL format
        '''
        cm=CountryManager("github",debug=True)
        cm.fromErdem()
        cm.store(cm.countryList)

    def testIntersection(self):
        '''
        test creating the intersection of a list of dictionaries
        '''
        list1 = [{'count': 351, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'},
         {'count': 332, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'},
         {'count': 336, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'},
         {'count': 359, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'},
         {'count': 309, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'}]

        list2 = [{'count': 359, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'},
             {'count': 351, 'evt_datetime': datetime.datetime(2015, 10, 23, 8, 45), 'att_value': 'red'},
             {'count': 381, 'evt_datetime': datetime.datetime(2015, 10, 22, 8, 45), 'att_value': 'red'}]

        listi=LOD.intersect(list1, list2,'count')
        print(listi)
        self.assertEquals(2,len(listi))
        listi=LOD.intersect(list1, list2)
        print(listi)
        self.assertEquals(2,len(listi))

if __name__ == "__main__":
    #import sys;sys.argv = ['', 'Test.testName']
    unittest.main()


from triton  import *
from pintool import *

# Output
#
#  $ ./triton ./src/examples/pin/callback_signals.py ./src/samples/others/signals
#  Signal 11 received on thread 0.
#  ========================== DUMP ==========================
#  rax:    0x00000000000000                        ((_ zero_extend 32) (_ bv234 32))
#  rbx:    0x00000000000000                        UNSET
#  rcx:    0x00000000001ba4                        ((_ zero_extend 32) ((_ extract 31 0) #81))
#  rdx:    0x0000000000000b                        ((_ sign_extend 32) ((_ extract 31 0) #34))
#  rdi:    0x00000000001ba4                        ((_ sign_extend 32) ((_ extract 31 0) #83))
#  rsi:    0x00000000001ba4                        ((_ sign_extend 32) ((_ extract 31 0) #90))
#  rbp:    0x007fff097e3540                        ((_ extract 63 0) #0)
#  rsp:    0x007fff097e3528                        (bvsub ((_ extract 63 0) #47) (_ bv8 64))
#  rip:    0x007f3fa0735ea7                        (_ bv139911251582629 64)
#  r8:     0x007f3fa0a94c80                        UNSET
#  r9:     0x007f3fb671b120                        UNSET
#  r10:    0x00000000000000                        UNSET
#  r11:    0x007f3fa0735e70                        UNSET
#  r12:    0x00000000400460                        UNSET
#  r13:    0x007fff097e3620                        UNSET
#  r14:    0x00000000000000                        UNSET
#  r15:    0x00000000000000                        UNSET
#  xmm0:   0x000000ff000000                        UNSET
#  xmm1:   0x2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f      UNSET
#  xmm2:   0x00000000000000                        UNSET
#  xmm3:   0x00ff000000ff00                        UNSET
#  xmm4:   0x000000000000ff                        UNSET
#  xmm5:   0x00000000000000                        UNSET
#  xmm6:   0x00000000000000                        UNSET
#  xmm7:   0x00000000000000                        UNSET
#  xmm8:   0x00000000000000                        UNSET
#  xmm9:   0x00000000000000                        UNSET
#  xmm10:  0x00000000000000                        UNSET
#  xmm11:  0x00000000000000                        UNSET
#  xmm12:  0x00000000000000                        UNSET
#  xmm13:  0x00000000000000                        UNSET
#  xmm14:  0x00000000000000                        UNSET
#  xmm15:  0x00000000000000                        UNSET
#  af:     0x00000000000000                        (ite (= (_ bv16 64) (bvand (_ bv16 64) (bvxor #12 (bvxor ((_ extract 63 0) #0) (_ bv16 64))))) (_ bv1 1) (_ bv0 1))
#  cf:     0x00000000000000                        (_ bv0 1)
#  df:     0x00000000000000                        UNSET
#  if:     0x00000000000001                        UNSET
#  of:     0x00000000000000                        (_ bv0 1)
#  pd:     0x00000000000001                        (ite (= (parity_flag ((_ extract 7 0) #73)) (_ bv0 1)) (_ bv1 1) (_ bv0 1))
#  sf:     0x00000000000000                        (ite (= ((_ extract 31 31) #73) (_ bv1 1)) (_ bv1 1) (_ bv0 1))
#  tf:     0x00000000000000                        UNSET
#  zf:     0x00000000000001                        (ite (= #73 (_ bv0 32)) (_ bv1 1) (_ bv0 1))

def signals(threadId, sig):
    print 'Signal %d received on thread %d.' %(sig, threadId)
    print '========================== DUMP =========================='
    regs = getParentRegisters()
    for reg in regs:
        value  = getCurrentRegisterValue(reg)
        exprId = getSymbolicRegisterId(reg)
        print '%s:\t%#016x\t%s' %(reg.getName(), value, (getSymbolicExpressionFromId(exprId).getAst() if exprId != SYMEXPR.UNSET else 'UNSET'))
    return

if __name__ == '__main__':

    # Set architecture
    setArchitecture(ARCH.X86_64)

    # Start the symbolic analysis from the Entry point
    startAnalysisFromEntry()

    # Add a callback.
    insertCall(signals, INSERT_POINT.SIGNALS)

    # Run the instrumentation - Never returns
    runProgram()


from core.room import Room

class Player():
    def __init__(self, current_room, inventory = []):
        # self.name = name
        self.current_room = current_room
        self.inventory = inventory

    def room_info(self):
        name = self.current_room.name
        description = self.current_room.description
        return f'{name} - {description}'

    def investigate(self):
        item = self.current_room.items
        if item != None:
            return f'You see a {item}.'
        else:
            return "There is nothing here."

    def remove_inventory(self):
        self.inventory = []


from argparse  import ArgumentParser
from ._version import __version__

def build_args_parser(
    prog: str,
    description: str = '',
    epilog: str = ''
) -> ArgumentParser:

    parser = ArgumentParser(
        prog = prog,
        description = description,
        epilog = epilog
    )

    # Build Parser
    parser = add_arguments(parser)

    return parser

def add_arguments(parser: ArgumentParser) -> ArgumentParser:
    parser.add_argument(
        'input',
        type=str,
        help='Path to an .xml SBOL file containing constructs designs and sequences'
    )
    parser.add_argument(
        'output',
        type=str,
        help='Path to the output spreadsheet'
    )
    parser.add_argument(
        'assembly_method',
        type=str,
        choices=["gibson", "golden_gate", "any_method"],
        help='If "any_method" is selected, each construct can be built with any method. However, Golden Gate Assembly will have priority over Gibson Assembly'
    )
    parser.add_argument(
        '--nb_constructs',
        type=int,
        help='Maximum number of constructs to build (only used in tests)'
    )
    parser.add_argument(
        '--version',
        action='version',
        version='%(prog)s {}'.format(__version__),
        help='show the version number and exit'
    )
    return parser


from Instrucciones.Declaracion import Declaracion
from Instrucciones.Sql_create.Tipo_Constraint import Tipo_Constraint, Tipo_Dato_Constraint
from Instrucciones.TablaSimbolos.Tipo import Tipo
from Instrucciones.TablaSimbolos.Instruccion import Instruccion
from Instrucciones.TablaSimbolos.Tabla import Tabla
from Instrucciones.Excepcion import Excepcion
from storageManager.jsonMode import *
from Instrucciones.Tablas.Tablas import Tablas
from Instrucciones.TablaSimbolos.Tipo import Tipo, Tipo_Dato
from Instrucciones.TablaSimbolos.Tipo import Tipo, Tipo_Dato
from Instrucciones.Tablas.Campo import Campo
from Optimizador.C3D import *
from Instrucciones.TablaSimbolos import Instruccion3D as c3d

class CreateTable(Instruccion):
    def __init__(self, tabla, tipo, campos, herencia, strGram ,linea, columna):
        Instruccion.__init__(self,tipo,linea,columna, strGram)
        self.tabla = tabla
        self.campos = campos
        self.herencia = herencia

    def ejecutar(self, tabla, arbol):
        super().ejecutar(tabla,arbol)
        # Ambito para la tabla
        tablaLocal = Tabla(tabla)
        compuesta = True
        #SE VALIDA QUE SE HAYA SELECCIONADO UN BD
        if arbol.bdUsar != None:
            for camp in self.campos:
                if isinstance(camp, Tipo_Constraint):
                    tc=self.campos.pop(int(self.campos.index(camp)))
                    if tc.tipo == Tipo_Dato_Constraint.UNIQUE or tc.tipo == Tipo_Dato_Constraint.PRIMARY_KEY or tc.tipo == Tipo_Dato_Constraint.FOREIGN_KEY:
                        for id in tc.expresion:
                            bid=False
                            for ct in self.campos:
                                if ct.nombre== id:
                                    if  self.campos[self.campos.index(ct)].constraint == None:
                                        self.campos[self.campos.index(ct)].constraint=[]
                                        if tc.tipo == Tipo_Dato_Constraint.UNIQUE:
                                            self.campos[self.campos.index(ct)].constraint.append(Tipo_Constraint(self.tabla+"_"+ct.nombre+"_pkey", Tipo_Dato_Constraint.UNIQUE, None))
                                        if tc.tipo == Tipo_Dato_Constraint.PRIMARY_KEY:
                                            compuesta = False
                                            self.campos[self.campos.index(ct)].constraint.append(Tipo_Constraint(self.tabla+"_pkey", Tipo_Dato_Constraint.PRIMARY_KEY, None))
                                        #if tc.tipo == Tipo_Dato_Constraint.FOREIGN_KEY:
                                            #self.campos[self.campos.index(ct)].constraint.append(Tipo_Constraint(None, Tipo_Dato_Constraint.UNIQUE, None))
                                    bid=True

                            if not bid:
                                error = Excepcion("42P10","Semantico",f"La columna <<{id}>> no existe, Error en el Constraint",self.linea,self.columna)
                                arbol.excepciones.append(error)
                                arbol.consola.append(error.toString())
                                return

            #SE VALIDA SI LA TABLA VA HEREDAR
            if self.herencia!=None:
                #SE BUSCA LA SI LA TABLA HEREDADA EXISTE
                htabla = arbol.devolverBaseDeDatos().getTabla(self.herencia)
                if htabla != None:
                    tabla_temp=[]
                    #SE RECORRE TODOS LAS COLUMNAS DE LA TABLA PARA UNIR CAMPOS REPETIDOS
                    for campo_her in htabla.lista_de_campos:
                        indice=0
                        bandera_campo=True
                        for campo_nuevo in self.campos:
                            if campo_her.nombre==campo_nuevo.nombre:
                                tabla_temp.append(campo_nuevo)
                                arbol.consola.append(f"NOTICE: mezclando la columna <<{campo_nuevo.nombre}>> con la definición heredada.")
                                self.campos.pop(indice)
                                indice+=1
                                bandera_campo=False
                                break
                        if bandera_campo:
                            tabla_temp.append(campo_her)
                    tabla_temp = tabla_temp + self.campos
                    self.campos= tabla_temp
                else:
                    error = Excepcion(f"42P01","Semantico","No existe la relación <<{self.herencia}>>.",self.linea,self.columna)
                    arbol.excepciones.append(error)
                    arbol.consola.append(error.toString())
                    return
            # VERIFICACIÓN LLAVES PRIMARIAS
            listaPrimarias = []
            for camp in self.campos:
                if isinstance(camp.tipo,Tipo):
                    if camp.constraint != None:
                        for s in camp.constraint:
                            if s.tipo == Tipo_Dato_Constraint.PRIMARY_KEY:
                                listaPrimarias.append(camp)
            if len(listaPrimarias) > 1 and compuesta:
                error = Excepcion("42P16","Semantico","No se permiten múltiples llaves primarias para la tabla «"+self.tabla+"»",self.linea,self.columna)
                arbol.excepciones.append(error)
                arbol.consola.append(error.toString())
                return error

            #SE CREA UN AMBITO PARA LA TABLA
            tablaNueva = Tablas(self.tabla,None)
            #SE LLENA LA TABLA EN MEMORIA
            for camp in self.campos:
                if isinstance(camp.tipo,Tipo):
                    if camp.tipo.tipo == Tipo_Dato.TIPOENUM:
                        existe = arbol.getEnum(camp.tipo.nombre)
                        if existe == None:
                            error = Excepcion('42P00',"Semántico","El tipo "+camp.tipo.nombre+" no existe",self.linea,self.columna)
                            arbol.excepciones.append(error)
                            arbol.consola.append(error.toString())
                            return error
                    if camp.constraint != None:
                        for s in camp.constraint:
                            if s.tipo == Tipo_Dato_Constraint.CHECK:
                                arbol.comprobacionCreate = True
                                objeto = Declaracion(camp.nombre, camp.tipo, s.expresion)
                                checkBueno = objeto.ejecutar(tablaLocal, arbol)
                                if not isinstance(checkBueno,Excepcion):
                                    if s.id == None:
                                        s.id = self.tabla+"_"+camp.nombre+"_"+"check1"
                                    #tablaNueva.agregarColumna(camp.nombre,camp.tipo.toString(),None, camp.constraint)
                                    #continue
                                    pass
                                else:
                                    #arbol.consola.append(checkBueno.toString())
                                    return
                            elif s.tipo == Tipo_Dato_Constraint.PRIMARY_KEY:
                                if s.id == None:
                                    s.id = self.tabla+"_pkey"
                            elif s.tipo == Tipo_Dato_Constraint.UNIQUE:
                                if s.id == None:
                                    s.id = self.tabla+"_"+camp.nombre+"_pkey"
                    tablaNueva.agregarColumna(camp.nombre,camp.tipo,None, camp.constraint)
                    #tablaNueva.lista_constraint.append(camp.constraint)
                else:
                    tablaNueva.agregarColumna(camp.nombre,camp.tipo,None, camp.constraint)
                    #tablaNueva.lista_constraint.append(camp.constraint)
            arbol.comprobacionCreate = False
            #SE CREA LA TABLA EN DISCO
            ctable = createTable(arbol.bdUsar,self.tabla,len(self.campos))

            if ctable==0: #CUANDO LA TABLA SE CREA CORRECTAMENTE
                arbol.consola.append(f"La Tabla: <<{self.tabla}>> se creo correctamente.")
                arbol.agregarTablaABd(tablaNueva)
            elif ctable==3: #CUANDO LA TABLA YA EXISTE
                error = Excepcion("100","Semantico","La Tabla ya Existe.",self.linea,self.columna)
                arbol.excepciones.append(error)
                arbol.consola.append(error.toString())
            elif ctable==2: #CUANDO POR ALGUN ERROR  NO SE CREA LA TABLA.
                error = Excepcion("100","Semantico","Error Interno.",self.linea,self.columna)
                arbol.excepciones.append(error)
                arbol.consola.append(error.toString())

            # SE AGREGAN LAS LLAVES PRIMARIAS A LA TABLA
            listaIndices = []
            resultado=0
            for i in listaPrimarias:
                listaIndices.append(tablaNueva.devolverColumna(i.nombre))
            if len(listaIndices) >0:
                #print("SE AGREGO UN INDICE")
                resultado = alterAddPK(arbol.getBaseDatos(), self.tabla, listaIndices)
            if resultado == 1:
                error = Excepcion('XX000',"Semántico","Error interno",self.linea,self.columna)
                arbol.excepciones.append(error)
                arbol.consola.append(error.toString())
                return error
            elif resultado == 2:
                error = Excepcion('42P00',"Semántico","La base de datos "+str(arbol.getBaseDatos())+" no existe",self.linea,self.columna)
                arbol.excepciones.append(error)
                arbol.consola.append(error.toString())
                return error
            elif resultado == 3:
                error = Excepcion('42P01',"Semántico","No existe la relación "+self.tabla,self.linea,self.columna)
                arbol.excepciones.append(error)
                arbol.consola.append(error.toString())
                return error
            elif resultado == 4:
                error = Excepcion('42P16',"Semántico","No se permiten múltiples llaves primarias para la tabla «"+self.tabla+"»",self.linea,self.columna)
                arbol.excepciones.append(error)
                arbol.consola.append(error.toString())
                return error
            elif resultado == 5:
                error = Excepcion('XX002',"Semántico","Columna fuera de limites."+self.tabla,self.linea,self.columna)
                arbol.excepciones.append(error)
                arbol.consola.append(error.toString())
                return error
        else:
            error = Excepcion("100","Semantico","No ha seleccionado ninguna Base de Datos.",self.linea,self.columna)
            arbol.excepciones.append(error)
            arbol.consola.append(error.toString())

    def generar3D(self, tabla, arbol):
        super().generar3D(tabla,arbol)
        code = []
        t0 = c3d.getTemporal()
        code.append(c3d.asignacionString(t0, "CREATE TABLE " + self.tabla + " (\\n"))

        sizeCol = len(self.campos)
        contador = 1
        for col in self.campos:
            if isinstance(col, Campo):
                sizeCol -= 1
            elif not isinstance(col, Campo):
                lista = col.generar3D(tabla, arbol)
                code += lista
                tLast = c3d.getLastTemporal()
                if contador != sizeCol:
                    t3 = c3d.getTemporal()
                    code.append(c3d.operacion(t3, Identificador(tLast), Valor('",\\n"', "STRING"), OP_ARITMETICO.SUMA))
                    contador += 1
                    tLast = t3
                t2 = c3d.getTemporal()
                code.append(c3d.operacion(t2, Identificador(t0), Identificador(tLast), OP_ARITMETICO.SUMA))
                t0 = t2

        t1 = c3d.getTemporal()
        if self.herencia != None:
            code.append(c3d.operacion(t1, Identificador(t0), Valor('"\\n) INHERITS (' + self.herencia + '"', "STRING"), OP_ARITMETICO.SUMA))
            t0 = t1
            t1 = c3d.getTemporal()
        code.append(c3d.operacion(t1, Identificador(t0), Valor('");"', "STRING"), OP_ARITMETICO.SUMA))
        code.append(c3d.asignacionTemporalStack(t1))
        code.append(c3d.aumentarP())

        return code

class IdentificadorColumna(Instruccion):
    def __init__(self, id, linea, columna):
        self.id = id
        Instruccion.__init__(self,Tipo(Tipo_Dato.ID),linea,columna,strGram)

    def ejecutar(self, tabla, arbol):
        super().ejecutar(tabla,arbol)
        variable = tabla.getVariable(self.id)
        if variable == None:
            error = Excepcion("42P10","Semantico","La columna "+str(self.id)+" no existe",self.linea,self.columna)
            arbol.excepciones.append(error)
            arbol.consola.append(error.toString())
            return error
        self.tipo = variable.tipo
        return variable.valor.ejecutar(tabla, arbol)

    def generar3D(self, tabla, arbol):
        super().generar3D(tabla,arbol)


import logging
import sys
from requests import HTTPError

from .readwritelock import ReadWriteLock
from .interfaces import CachePolicy

log = logging.getLogger(sys.modules[__name__].__name__)

class ManualPollingCachePolicy(CachePolicy):
    def __init__(self, config_fetcher, config_cache):
        self._config_fetcher = config_fetcher
        self._config_cache = config_cache
        self._lock = ReadWriteLock()

    def get(self):
        try:
            self._lock.acquire_read()

            config = self._config_cache.get()
            return config
        finally:
            self._lock.release_read()

    def force_refresh(self):
        force_fetch = False

        try:
            self._lock.acquire_read()
            config = self._config_cache.get()
            force_fetch = not bool(config)
        finally:
            self._lock.release_read()

        try:
            configuration_response = self._config_fetcher.get_configuration_json(
                force_fetch
            )
            if configuration_response.is_fetched():
                configuration = configuration_response.json()
                try:
                    self._lock.acquire_write()
                    self._config_cache.set(configuration)
                finally:
                    self._lock.release_write()

        except HTTPError as e:
            log.error(
                "Double-check your SDK Key at https://app.configcat.com/sdkkey."
                " Received unexpected response: [%s]" % str(e.response)
            )
        except:
            log.exception(sys.exc_info()[0])

    def stop(self):
        pass


"""
This module contains callbacks used during the test phase.
"""
from torchlite.data.datasets import ImageDataset
from tqdm import tqdm

class TestCallback:
    def __init__(self):
        self.validation_data = None
        self.params = None
        self.model = None

    def on_test_begin(self, logs=None):
        pass

    def on_test_end(self, logs=None):
        pass

    def on_batch_begin(self, batch, logs=None):
        pass

    def on_batch_end(self, batch, logs=None):
        pass

class TestCallbackList(object):
    """Container abstracting a list of callbacks.
    Args:
        callbacks: List of `Callback` instances.
        queue_length: Queue length for keeping
            running statistics over callback execution time.
    """

    def __init__(self, callbacks=None, queue_length=10):
        callbacks = callbacks or []
        self.callbacks = [c for c in callbacks]
        self.queue_length = queue_length

    def append(self, callback):
        assert isinstance(callback, TestCallback), \
            "Your callback is not an instance of TestCallback: {}".format(callback)
        self.callbacks.append(callback)

    def on_test_begin(self, logs=None):
        """Called at the beginning of testing.
        Args:
            logs: dictionary of logs.
        """
        logs = logs or {}
        for callback in self.callbacks:
            callback.on_test_begin(logs)

    def on_test_end(self, logs=None):
        """Called at the end of testing.
        Args:
            logs: dictionary of logs.
        """
        logs = logs or {}
        for callback in self.callbacks:
            callback.on_test_end(logs)

    def on_batch_begin(self, batch, logs=None):
        """Called right before processing a batch.
        Args:
            batch: integer, index of batch within the current epoch.
            logs: dictionary of logs.
        """
        logs = logs or {}
        for callback in self.callbacks:
            callback.on_batch_begin(batch, logs)

    def on_batch_end(self, batch, logs=None):
        """Called at the end of a batch.
        Args:
            batch: integer, index of batch within the current epoch.
            logs: dictionary of logs.
        """
        logs = logs or {}
        for callback in self.callbacks:
            callback.on_batch_end(batch, logs)

    def __iter__(self):
        return iter(self.callbacks)

class TQDM(TestCallback):
    def __init__(self):
        super().__init__()
        self.pbar = None

    def on_test_begin(self, logs=None):
        test_loader_len = len(logs["loader"])
        self.pbar = tqdm(total=test_loader_len, desc="Classifying")

    def on_batch_end(self, batch, logs=None):
        self.pbar.update(1)

    def on_test_end(self, logs=None):
        print()

class ActivationMapVisualizerCallback(TestCallback):
    def __init__(self, filename):
        """
            Store an image with heatmap activations in a heatmaps list
            using the Grad_cam++ technique: https://arxiv.org/abs/1710.11063
            # TODO may combines with TensorboardVisualizer?

            /!\ This technique only works with image torchlite.data.datasets.ImagesDataset
        Args:
            filename (str): The file name that you want to visualize
        """
        super().__init__()
        self.filename = filename
        self.heatmap = None

    def on_test_end(self, logs=None):
        model = self.model
        ds = logs["loader"].dataset if logs["loader"] else None
        assert isinstance(ds, ImageDataset), \
            "ActivationMapVisualizer: The loader is not an instance of torchlite.data.datasets.ImagesDataset"
        image, label, _ = ds.get_by_name(self.filename)
        # TODO finish grad cam here https://github.com/adityac94/Grad_CAM_plus_plus/blob/master/misc/utils.py#L51

    @property
    def get_heatmap(self):
        return self.heatmap

class TTACallback(TestCallback):
    def __init__(self):
        """
        Test time augmentation callback
        """
        # TODO implement https://github.com/fastai/fastai/blob/master/fastai/learner.py#L242
        super().__init__()


from .util import Configurable, Openable, pretty_str

@pretty_str
class Hook(Configurable, Openable):
    """
    Base of all hook classes, performs any form of processing on messages from all connected
    plugs, via the provided host instance.

    Instantiation may raise :class:`.ConfigError` if the provided configuration is invalid.

    Attributes:
        virtual (bool):
            ``True`` if managed by another component (e.g. a hook that exposes plug functionality).
    """

    def __init__(self, name, config, host, virtual=False):
        super().__init__(name, config, host)
        self.virtual = virtual

    async def start(self):
        """
        Perform any setup tasks.
        """

    async def stop(self):
        """
        Perform any teardown tasks.
        """

    def on_load(self):
        """
        Perform any additional one-time setup that requires other plugs or hooks to be loaded.
        """

    async def channel_migrate(self, old, new):
        """
        Move any private data between channels on admin request.  This is intended to cover data
        keyed by channel sources and plug network identifiers.

        Args:
            old (.Channel):
                Existing channel with local data.
            new (.Channel):
                Target replacement channel to migrate data to.

        Returns:
            bool:
                ``True`` if any data was migrated for the requested channel.
        """
        return False

    async def before_send(self, channel, msg):
        """
        Modify an outgoing message before it's pushed to the network.  The ``(channel, msg)`` pair
        must be returned, so hooks may modify in-place or return a different pair.  This method is
        called for each hook, one after another.  If ``channel`` is modified, the sending will
        restart on the new channel, meaning this method will be called again for all hooks.

        Hooks may also suppress a message (e.g. if their actions caused it, but it bears no value
        to the network) by returning ``None``.

        Args:
            channel (.Channel):
                Original source of this message.
            msg (.Message):
                Raw message received from another plug.

        Returns:
            (.Channel, .Message) tuple:
                The augmented or replacement pair, or ``None`` to suppress this message.
        """
        return (channel, msg)

    async def before_receive(self, sent, source, primary):
        """
        Modify an incoming message before it's pushed to other hooks.  The ``sent`` object must be
        returned, so hooks may modify in-place or return a different object.  This method is called
        for each hook, one after another, so any time-consuming tasks should be deferred to
        :meth:`process` (which is run for all hooks in parallel).

        Hooks may also suppress a message (e.g. if their actions caused it, but it bears no value
        to the rest of the system) by returning ``None``.

        Args:
            sent (.SentMessage):
                Raw message received from another plug.
            source (.Message):
                Original message data used to generate the raw message, if sent via the plug (e.g.
                from another hook), equivalent to ``msg`` if the source is otherwise unknown.
            primary (bool):
                ``False`` for supplementary messages if the source message required multiple raw
                messages in order to represent it (e.g. messages with multiple attachments where
                the underlying network doesn't support it), otherwise ``True``.

        Returns:
            .SentMessage:
                The augmented or replacement message, or ``None`` to suppress this message.
        """
        return sent

    async def on_receive(self, sent, source, primary):
        """
        Handle an incoming message received by any plug.

        Args:
            sent (.SentMessage):
                Raw message received from another plug.
            source (.Message):
                Original message data used to generate the raw message, if sent via the plug (e.g.
                from another hook), equivalent to ``msg`` if the source is otherwise unknown.
            primary (bool):
                ``False`` for supplementary messages if the source message required multiple raw
                messages in order to represent it (e.g. messages with multiple attachments where
                the underlying network doesn't support it), otherwise ``True``.
        """

    def on_config_change(self, source):
        """
        Handle a configuration change from another plug or hook.

        Args:
            source (.Configurable):
                Source plug or hook that triggered the event.
        """

    def __repr__(self):
        return "<{}: {}>".format(self.__class__.__name__, self.name)

class ResourceHook(Hook):
    """
    Variant of hooks that globally provide access to some resource.

    Only one of each class may be loaded, which happens before regular hooks, and such hooks are
    keyed by their class rather than a name, allowing for easier lookups.
    """