def load_config(self): '''(internal) This function is used for returning a ConfigParser with the application configuration. It's doing 3 things: #. Creating an instance of a ConfigParser #. Loading the default configuration by calling :meth:`build_config`, then #. If it exists, it loads the application configuration file, otherwise it creates one. :return: :class:`~kivy.config.ConfigParser` instance ''' try: config = ConfigParser.get_configparser('app') except KeyError: config = None if config is None: config = ConfigParser(name='app') self.config = config self.build_config(config) # if no sections are created, that's mean the user don't have # configuration. if len(config.sections()) == 0: return # ok, the user have some sections, read the default file if exist # or write it ! filename = self.get_application_config() if filename is None: return config Logger.debug('App: Loading configuration <{0}>'.format(filename)) if exists(filename): try: config.read(filename) except: Logger.error('App: Corrupted config file, ignored.') config.name = '' try: config = ConfigParser.get_configparser('app') except KeyError: config = None if config is None: config = ConfigParser(name='app') self.config = config self.build_config(config) pass else: Logger.debug( 'App: First configuration, create <{0}>'.format(filename)) config.filename = filename config.write() return config
def get_first_sub(self): config = ConfigParser() config.read('mysteryonline.ini') try: return self.sublocations[str(config.get('other', 'last_sublocation'))].name except KeyError: return self.list_sub()[0]
def update_panel(self): '''Update the MenuSidebar ''' self.config_parsers = {} self.interface.menu.buttons_layout.clear_widgets() for _file in os.listdir(self.PROFILES_PATH): _file_path = os.path.join(self.PROFILES_PATH, _file) config_parser = ConfigParser() config_parser.read(_file_path) prof_name = config_parser.getdefault('profile', 'name', 'PROFILE') if not prof_name.strip(): prof_name = 'PROFILE' self.config_parsers[str(prof_name) + '_' + _file_path] = config_parser for _file in sorted(self.config_parsers): prof_name = self.config_parsers[_file].getdefault( 'profile', 'name', 'PROFILE') if not prof_name.strip(): prof_name = 'PROFILE' self.add_json_panel( prof_name, self.config_parsers[_file], os.path.join(get_kd_data_dir(), 'settings', 'build_profile.json')) # force to show the first profile first_panel = self.interface.menu.buttons_layout.children[-1].uid self.interface.content.current_uid = first_panel
def reapply_version(app: MyDevoirsApp) -> Tuple[int, str]: """ Verifie les differents version précendentes :param app: L'instance en cours :return: 0: le fichier n'existe pas 1: la version a du être ajoutée 2: la version existe == version en cours 3: la version existe < version en cours 4: la version existe > version en cours """ cf_file = app.get_application_config() file = Path(cf_file) return_value = 0 file_version = None if file.is_file(): # pragma: no branch config = ConfigParser() try: config.read(cf_file) file_version = config.get("aide", "version") except NoSectionError: return_value = 1 except NoOptionError: return_value = 1 if file_version is not None: if file_version < VERSION: return_value = 3 elif file_version > VERSION: return_value = 4 else: return_value = 2 return return_value, file_version
def __init__(self, name): self.name = name self.path = "characters/{0}/".format(self.name) self.display_name = None self.series = None self.extra_series = [] self.sprites_path = None self.icons_path = None self.avatar = None self.loaded_sprites = False self.loaded_icons = False self.sprites = None self.icons = None self.link = None self.version = None # Hash tables for faster membership checking self.nsfw_sprites = {} self.spoiler_sprites = {} self.cg_sprites = {} try: self.config = ConfigParser(self.name) except ValueError: self.config = ConfigParser.get_configparser(self.name) try: self.read_config() except (KeyError, AttributeError): Logger.exception('Problematic character located in: ' + self.path)
def test_on_config_change_calls_func(self): self.app.on_config_change_bla = MagicMock() c = ConfigParser() option = ["bla", "cle", "value"] self.app.on_config_change(c, *option) assert self.app.on_config_change_bla.called assert self.app.on_config_change_bla.call_args == call(c, *option)
def __init__(self, **kvargs): super(Gllearn, self).__init__(**kvargs) Window.bind(on_keyboard=self.events_program) Window.soft_input_mode = 'below_target' self.list_previous_screens = ['base'] self.window = Window self.config = ConfigParser() self.manager = None self.window_language = None self.window_game_language = None self.exit_interval = False self.dict_language = literal_eval( open(os.path.join(self.directory, 'data', 'locales', 'locales.txt')).read()) self.translation = Translation( self.lang, 'Gllearn', os.path.join(self.directory, 'data', 'locales')) self.translation_game = Translation( self.lang_games, 'Games', os.path.join(self.directory, 'data', 'locales')) self.snake_words_with_color = [{ word: get_random_color(alpha=1.0) for word in words } for words in [ s.split(' ') for s in self.translation_game._('snake_rounds').split(' | ') ]] self.current_round_snake = 0
def right_click(self, inst, touch): if touch.button == 'right': try: if inst.collide_point(touch.x, touch.y): favorite = App.get_running_app().get_fav_chars() favorite.options = characters config = ConfigParser() config.read('mysteryonline.ini') fav_list = str(config.get('other', 'fav_characters').strip('[]')) fav_list = fav_list.replace("'", "") fav_list = fav_list.split(',') fav_list = [x.strip() for x in fav_list] for option in favorite.options: state = 'down' if option in favorite.value and option in fav_list else 'normal' btn = ToggleButton(text=option, state=state, size_hint_y=None, height=50) favorite.buttons.append(btn) for btn in favorite.buttons: if btn.text is characters[inst.name].name: if btn.state == 'normal': btn.state = 'down' else: btn.state = 'normal' favorite.value = [btn.text for btn in favorite.buttons if btn.state == 'down'] favorite.buttons.clear() self.save.is_saved = False self.main_lay.clear_widgets() self.fill_with_chars() except AttributeError: pass
def test_refresh_photolist(self): app = PhotoManager(data_directory=".") app.config = ConfigParser(interpolation=None) app.build_config(app.config) app.theme = Theme(app) app.build() app.show_import() # render(self.app) from screens.screenAlbum import ScreenAlbum, Album, Tag screen_album = ScreenAlbum(name='test_album') self.assertIsInstance(screen_album, ScreenAlbum) # # need to mock the photo object, but I don't know how: # screen_album.photo = '21.jpg' # screen_album.refresh_photolist() screen_album_album = Album(name='Album_album') self.assertIsInstance(screen_album_album, ScreenAlbum) screen_album_album.refresh_photolist() self.assertIsNotNone(screen_album_album.photos) screen_album_tag = Tag(name='Album_album') self.assertIsInstance(screen_album_tag, ScreenAlbum) # it seems that the implementation of PhotoManager is incomplete to support the following case: # screen_album_tag.refresh_photolist() print("bye") pass
def __init__(self, **kwargs): super().__init__(**kwargs) Window.bind(on_keyboard=self.events_program) Window.soft_input_mode = "below_target" self.LIST_SCREENS = [ "base", "license", "about", "server_lists_screen", "syncscreen", "server_readinglists_screen", "single_file_screen", "open_file_screen", ] self.list_previous_screens = ["base"] self.window = Window self.config = ConfigParser() self.manager = None self.window_language = None self.exit_interval = False self.comic_thumb_height = 240 self.comic_thumb_width = 156 self.dict_language = literal_eval( open(os.path.join(self.directory, "data", "locales", "locales.txt")).read()) # self.translation = Translation( # self.lang, 'Ttest', os.path.join(self.directory, # data', 'locales') # ) self.base_url = "" self.settings_cls = MySettings self.md_manager = None self.open_comic_screen = ""
def setUp(self, no_db=False): super().setUp() if not hasattr(self, "app"): self.app = App() Path(self.app.get_application_config()).unlink(missing_ok=True) if not self.app.config: self.app.config = ConfigParser() self.app.build_config(self.app.config) config = self.app.config for section, values in DEFAULT_SETTINGS.items(): config.setdefaults(section, values) if not no_db: init_update_matiere(db, reset=True) with db_session: for entity in db.entities.values(): if entity.__name__ != "Matiere": delete(e for e in entity) self.T = TempFile() EventLoop.ensure_window() self.window = EventLoop.window if self.TIMER: self.debut_time = time.time()
def build(self): #self.config = Config.get_configparser('app') self.config = ConfigParser(name='tetris_cfg') self.config.read('tetris.ini') #print "app cfg", self.config.filename #parser = ConfigParser.get_configparser('app') #clr = parser.get("color", "root_bgclr").encode("utf-8") clr = self.config.getdefault("color", "root_bgclr", '1,1,1') m = re.search(r'.*?(\d+).*,.*?(\d+).*,.*?(\d+).*', clr) self.root_bgclr_r = float(m.groups()[0])/255 self.root_bgclr_g = float(m.groups()[1])/255 self.root_bgclr_b = float(m.groups()[2])/255 clr = self.config.getdefault("color", "activity_bgclr", '1,1,1') m = re.search(r'.*?(\d+).*,.*?(\d+).*,.*?(\d+).*', clr) self.activity_bgclr_r = float(m.groups()[0])/255 self.activity_bgclr_g = float(m.groups()[1])/255 self.activity_bgclr_b = float(m.groups()[2])/255 # self.length = parser.getint("tetris", "length") # self.activity_rows = parser.getint("tetris", "activity_rows") # self.activity_cols = parser.getint("tetris", "activity_cols") self.anchor_x = self.config.getdefaultint("tetris", "anchor_x", 50) self.anchor_y = self.config.getdefaultint("tetris", "anchor_y", 50) self.length = self.config.getdefaultint("tetris", "length", 50) self.activity_rows = self.config.getdefaultint("tetris", "activity_rows", 24) self.activity_cols = self.config.getdefaultint("tetris", "activity_cols", 12) self.test = self.config.getdefaultint("tetris", "test", 100) logging.debug("[%d] [%d] [%d] [%d]", self.length, self.activity_rows, self.activity_cols, self.test) rootWidget = RootWidget() rootWidget.StartGame() return rootWidget
def on_ready(self, *args): """Called when mainscreen becomes active""" self.msg_input.ready(self) self.left_tab.ready(self) self.ooc_window.ready(self) self.log_window.ready() user_handler = App.get_running_app().get_user_handler() locations = location_manager.get_locations() config = ConfigParser() config.read('mysteryonline.ini') try: user_handler.set_current_loc(locations[str( config.get('other', 'last_location'))]) self.sprite_settings.update_sub(locations[str( config.get('other', 'last_location'))]) except KeyError: user_handler.set_current_loc(locations['Hakuryou']) self.sprite_settings.update_sub(locations['Hakuryou']) App.get_running_app().get_main_screen().log_window.add_entry( "You moved to %s.\n" % user_handler.get_current_loc().name) self.toolbar.set_user(self.user) self.toolbar.create_sfx_dropdown() self.sprite_preview.set_subloc(user_handler.get_chosen_subloc()) char = self.user.get_char() if char is not None: self.on_new_char(char) self.sprite_window.set_subloc(user_handler.get_chosen_subloc()) App.get_running_app().keyboard_listener.bind_keyboard()
def load(self): print("the data dir " + self.data_dir) self.config = ConfigParser() self.config.read(os.path.join(self.data_dir, 'preferences.ini')) self.set_config_defaults() self._prefs_dict = {'range_alerts': {}, 'gauge_settings': {}} try: with open(self.prefs_file, 'r') as data: content = data.read() content_dict = json.loads(content) if content_dict.has_key("range_alerts"): for name, settings in content_dict[ "range_alerts"].iteritems(): self._prefs_dict["range_alerts"][ name] = Range.from_dict(settings) if content_dict.has_key("gauge_settings"): for id, channel in content_dict[ "gauge_settings"].iteritems(): self._prefs_dict["gauge_settings"][id] = channel except Exception: pass
def __init__(self, **kwargs): super().__init__(**kwargs) self.selected_dampers = [ ] # Every damper selected by MyRightCheckbox add to this list. self.all_dampers_in_container = [ ] # Consists of all adding DamperListItem. self.damper = None self.dampers = [ ] # Has all getting dampers (class Damper) from the DB. self.found_dampers = [] # Has all found in searching dampers. self.menu_sort = None self.menu_dots = None # For exit on double tap on the buttnon back. self.is_back_clicked_once = False # My config. self.config = ConfigParser() # App theme. self.primary_palette = "Teal" self.accent_palette = "Amber" self.theme_style = "Light" # To avoid multi chosen right_checkbox_lang. self.lang_checkboxes_dict = dict() # Handling the back button. Window.bind(on_keyboard=self.key_input) # The current target TextInput widget requesting the keyboard # is presented just above the soft keyboard. Window.softinput_mode = "below_target"
def __init__(self, **kvargs): super(PyConversations, self).__init__(**kvargs) Window.bind(on_keyboard=self.events_program) Window.soft_input_mode = 'below_target' # self.admob_id = 'ca-app-pub-8865711525352558~2442322860' # self.banner_id = '' # self.interstitial_id = '' # self.ads = KivMob(self.admob_id) self.list_previous_screens = ['base'] self.window = Window self.config = ConfigParser() self.manager = None self.window_language = None self.exit_interval = False self.dict_language = literal_eval( open( os.path.join(self.directory, 'data', 'locales', 'locales.txt')).read() ) self.translation = Translation( self.lang, 'Ttest', os.path.join(self.directory, 'data', 'locales') ) self.start_time = None
def check_config(self): """ Checks to see if the config has the required XMPP fields filled out accordingly. Then, it evaluates the config file to make sure that all fields exist, at least corresponding to the example config file. """ conf = self.configuration if conf.has_section('xmpp'): if all( conf.has_option('xmpp', k) and conf.get('xmpp', k) for k in MANDATORY_XMPP_OPTIONS): self.xmpp_config_ok = True def_conf = ConfigParser() def_conf.read(DEFAULT_CONFIG_PATH) for section in def_conf.sections(): if conf.has_section(section): for option in def_conf.options(section): if not conf.has_option(section, option) or conf.get( section, option) is None: conf.set(section, option, def_conf.get(section, option)) else: conf.add_section(section) for option in def_conf.options(section): conf.set(section, option, def_conf.get(section, option)) self.configuration = conf self.configuration.write()
def load_proj_settings(self): '''This function loads project settings ''' self.config_parser = ConfigParser() file_path = os.path.join(self.project.path, PROJ_CONFIG) if not os.path.exists(file_path): if not os.path.exists(os.path.dirname(file_path)): os.makedirs(os.path.dirname(file_path)) CONFIG_TEMPLATE = '''[proj_name] name = Project [arguments] arg = [env variables] env = ''' f = open(file_path, 'w') f.write(CONFIG_TEMPLATE) f.close() self.config_parser.read(file_path) _dir = os.path.dirname(designer.__file__) _dir = os.path.split(_dir)[0] settings_dir = os.path.join(_dir, 'designer', 'settings') self.add_json_panel( 'Shell Environment', self.config_parser, os.path.join(settings_dir, 'proj_settings_shell_env.json')) self.add_json_panel( 'Project Properties', self.config_parser, os.path.join(settings_dir, 'proj_settings_proj_prop.json'))
def load_story_config(self, library_dir): self.story_config = ConfigParser() story_file_loc = library_dir.joinpath(self.title + '.ini') if not story_file_loc.is_file(): self.story_config.setdefaults('metadata', get_metadata_defaults(self.title, self.library_parent)) self.story_config.setdefaults('title', get_page_defaults(self.title)) self.story_config_file = str(story_file_loc) # Set config from story's config file. self.story_config.read(str(self.story_config_file)) if self.story_config.get('metadata', 'story') != self.title: self.story_config.set('metadata', 'story', self.title) if self.story_config.get('metadata', 'library') != self.library_parent: self.story_config.set('metadata', 'library', self.library_parent) # Find the media location for this story's title page self.title_media_location = self.story_config.get('title', 'media_location') # Find all the pages self.pages = ['title'] + [x.strip() for x in self.story_config.get('metadata', 'pages').split(',')] self.story_config.write()
def __init__(self, **kwargs): super(NuBippyApp, self).__init__(**kwargs) self.isPopup = False self.show_info = False self.use_kivy_settings = False # load config file # we don't display the settings panel, changes can be made manually to the ini file self.config = ConfigParser() self.config.read('nubippy.ini') # Set the language and load the language file self.language = self.config.get('Language', 'active_language') try: self.lang = json.load(open('res/json/languages/' + self.language + '.json', 'r')) except ValueError as e: print('') print('##################################################################') print('') print('There was an Error loading the ' + self.language + ' language file.') print('') print(str(e)) print('') print('##################################################################') raise SystemExit return
def update_sub(self, loc): if self.subloc_btn is not None: self.subloc_drop.clear_widgets() fav_subloc = App.get_running_app().get_fav_subloc() config = ConfigParser() config.read('mysteryonline.ini') fav_list = str(config.get('other', 'fav_subloc').strip('[]')) fav_list = fav_list.replace("'", "") fav_list = fav_list.split(',') fav_list = [x.strip() for x in fav_list] for sub in loc.list_sub(): if loc.name + '_' + sub in fav_list and loc.name + '_' + sub in fav_subloc.value: btn = Button( text=sub, size_hint=(None, None), size=(200, 30), background_normal= 'atlas://data/images/defaulttheme/button_pressed', background_down='atlas://data/images/defaulttheme/button') btn.bind( on_release=lambda btn_: self.subloc_drop.select(btn_.text)) self.subloc_drop.add_widget(btn) for sub in loc.list_sub(): if loc.name + '_' + sub not in fav_subloc.value or loc.name + '_' + sub not in fav_list: btn = Button(text=sub, size_hint=(None, None), size=(200, 30)) btn.bind( on_release=lambda btn_: self.subloc_drop.select(btn_.text)) self.subloc_drop.add_widget(btn) self.subloc_btn.text = loc.get_first_sub()
def build_settings(self, req, result): self.settings = None self.settings = SettingsWithNoMenu() self.settings.on_config_change = self.on_config_change self.settings.register_type('string_long', SettingString) self.settings.register_type('num_int', SettingNumeric) self.settings.register_type('num', SettingNumeric) config = ConfigParser() print result['settings'] config.setdefaults(self.popup.device_id, result['settings']) self.settings.add_json_panel(result['name'], config, data=dumps(result['settings_format'])) self.add_widget(self.settings) buttons = BoxLayout(orientation='horizontal') buttons.add_widget( Button(text='Previous', on_release=self.popup.previous_view, height='50dp')) buttons.add_widget( Button(text='Next', on_release=self.popup.next_view, height='50dp')) self.add_widget(buttons)
class SpikeGlApp(App): Config = ConfigParser(name='Spikes') def __init__(self, **kwargs): Window.size = (1024, 500) Window.minimum_width = 900 Window.minimum_height = 600 Window.x = 0 Window.y = 0 resource_path = os.path.join( os.path.dirname(os.path.realpath(__file__)), 'resources') resource_add_path(resource_path) resource_add_path(os.path.join(resource_path, 'shaders')) super(SpikeGlApp, self).__init__(**kwargs) Config.set("input", "mouse", "mouse,disable_multitouch") Config.set("kivy", "exit_on_escape", 0) Logger.info("Starting up") def build(self): return (Main()) def exit_app(self, *args): self.shutdown() def shutdown(self, *args): exit()
class PeachyScannerApp(App): button_height = NumericProperty(dp(40)) label_height = NumericProperty(dp(30)) input_height = NumericProperty(dp(30)) refresh_rate = NumericProperty(1.0 / 30.0) Config = ConfigParser(name='PeachyScanner') scanner = ObjectProperty() def __init__(self, scanner, **kwargs): Window.size = (1024, 500) Window.minimum_width = 900 Window.minimum_height = 600 Window.x = 0 Window.y = 0 resource_path = os.path.join( os.path.dirname(os.path.realpath(__file__)), 'resources') resource_add_path(resource_path) resource_add_path(os.path.join(resource_path, 'shaders')) resource_add_path(os.path.join(resource_path, 'images')) self.scanner = scanner super(PeachyScannerApp, self).__init__(**kwargs) Config.set("input", "mouse", "mouse,disable_multitouch") Config.set("kivy", "exit_on_escape", 0) Logger.info("Starting up") def build(self): return (MasterGUI(self.scanner)) def exit_app(self, *args): self.shutdown() def shutdown(self, *args): exit()
def read_spoiler_sprites(self): try: spoiler_section = self.config['spoiler'] except KeyError: return spoiler_list = [] series = self.extra_series[:] series.insert(0, self.series) config = ConfigParser() config.read('mysteryonline.ini') try: whitelist = config.get('other', 'whitelisted_series') except: for key, s in zip(sorted(spoiler_section), series): spoiler_sprites = spoiler_section[key].split(',') spoiler_list.extend(spoiler_sprites) for sprite_name in spoiler_list: self.spoiler_sprites[sprite_name] = None return whitelist = whitelist.strip('[]') whitelist = whitelist.replace("'", "") whitelist = whitelist.split(',') whitelist = [x.strip() for x in whitelist] for key, s in zip(sorted(spoiler_section), series): if s not in whitelist: spoiler_sprites = spoiler_section[key].split(',') spoiler_list.extend(spoiler_sprites) for sprite_name in spoiler_list: self.spoiler_sprites[sprite_name] = None
def build_config(self, _) -> None: """ """ self.config = ConfigParser() self.config.read('config.ini') self.config.setdefaults('general', { 'userdir': 'user', 'autosave': True }) self.config.setdefaults( 'ai', { 'timeout': 20.0, 'memory': 20, 'max_length': 60, 'beam_searches': 1, 'temperature': 0.8, 'top_k': 40, 'top_p': 0.9, 'repetition_penalty': 1.1 }) self.config.setdefaults( 'modules', { 'input_filters': 'aiventure:filters', 'output_filters': 'aiventure:filters', 'display_filter': 'aiventure:filters' }) self.config.write()
def load_proj_settings(self): '''This function loads project settings ''' self.config_parser = ConfigParser() file_path = os.path.join(self.proj_loader.proj_dir, PROJ_CONFIG) if not os.path.exists(file_path): if not os.path.exists(os.path.dirname(file_path)): os.makedirs(os.path.dirname(file_path)) CONFIG_TEMPLATE = '''[proj_name] name = Project [arguments] arg = [env variables] env = ''' f = open(file_path, 'w') f.write(CONFIG_TEMPLATE) f.close() self.config_parser.read(file_path) proj_prop_panel = self.create_json_panel( 'Project Properties', self.config_parser, './designer/settings/proj_settings_proj_prop.json') self.add_widget(proj_prop_panel) self.add_json_panel( 'Shell Environment', self.config_parser, './designer/settings/proj_settings_shell_env.json')
def load_config(self): '''(internal) This function is used for returning a ConfigParser with the application configuration. It's doing 3 things: #. Create an instance of a ConfigParser #. Load the default configuration by calling :meth:`build_config`, then #. If exist, load the application configuration file, or create it if it's not existing. :return: ConfigParser instance ''' self.config = config = ConfigParser() self.build_config(config) # if no sections are created, that's mean the user don't have # configuration. if len(config.sections()) == 0: return # ok, the user have some sections, read the default file if exist # or write it ! filename = self.get_application_config() if filename is None: return config if exists(filename): config.read(filename) else: config.filename = filename config.write() return config
def load_settings(self): '''This function loads project settings ''' self.config_parser = ConfigParser() DESIGNER_CONFIG = os.path.join(get_kivy_designer_dir(), DESIGNER_CONFIG_FILE_NAME) _dir = os.path.dirname(designer.__file__) _dir = os.path.split(_dir)[0] DEFAULT_CONFIG = os.path.join(_dir, DESIGNER_CONFIG_FILE_NAME) if not os.path.exists(DESIGNER_CONFIG): shutil.copyfile(DEFAULT_CONFIG, DESIGNER_CONFIG) self.config_parser.read(DESIGNER_CONFIG) self.config_parser.upgrade(DEFAULT_CONFIG) self.add_json_panel( 'Kivy Designer Settings', self.config_parser, os.path.join(_dir, 'designer', 'settings', 'designer_settings.json')) path = self.config_parser.getdefault('global', 'python_shell_path', '') if path == "": self.config_parser.set('global', 'python_shell_path', sys.executable) self.config_parser.write()
def __init__(self, **kwargs): super(MyConnectDialog, self).__init__(**kwargs) self.register_type('buttons', SettingButtons) self.register_event_type('on_connect') config = ConfigParser() config.setdefaults( 'common', { 'conf_role': 'SERVER', 'conf_transport': 'TLS', 'conf_ip': '192.168.1.220', 'conf_serport': 'COM1' }) config.setdefaults('client', { 'conf_user': '******', 'conf_cert_chain': 'myhome-bundle.crt' }) config.setdefaults('server', { 'conf_serv_cert': 'rootca.crt', 'conf_serv_key': 'secret/rootca.key' }) self.myconfig = config try: config.read("i-spy.myconfig") except: print("No i-spy.myconfig") self.add_json_panel('IO device connect', config, data=json)