示例#1
0
    def OnBuildCreatePackage(self, stage, targetFile):
        Logger.Debug(__name__,
                     GT(u'Creating {} from {}').format(targetFile, stage))

        packager = GetExecutable(u'dpkg-deb')
        fakeroot = GetExecutable(u'fakeroot')

        if not fakeroot or not packager:
            return (dbrerrno.ENOENT, GT(u'Cannot run "fakeroot dpkg'))

        packager = os.path.basename(packager)

        Logger.Debug(__name__, GT(u'System packager: {}').format(packager))

        # DEBUG:
        cmd = u'{} {} -b "{}" "{}"'.format(fakeroot, packager, stage,
                                           targetFile)
        Logger.Debug(__name__, GT(u'Executing: {}').format(cmd))

        output = GetCommandOutput(fakeroot, (
            packager,
            u'-b',
            stage,
            targetFile,
        ))

        Logger.Debug(__name__, GT(u'Build output: {}').format(output))

        return output
示例#2
0
 def ExportDeprecated(self, outDir, outName=wx.EmptyString):
     if not os.path.isdir(outDir):
         Logger.Debug(__name__, u'Directory does not exist: {}'.format(outDir))
         return ERR_DIR_NOT_AVAILABLE
     
     if outName == wx.EmptyString:
         outName = page_ids[self.GetId()].upper()
     
     page_info = self.Get()
     
     if not page_info:
         return 0
     
     if not outName:
         outName = self.Name
     
     if TextIsEmpty(page_info):
         return 0
     
     absolute_filename = u'{}/{}'.format(outDir, outName)
     
     Logger.Debug(outName, u'Exporting: {}'.format(absolute_filename))
     
     WriteFile(absolute_filename, page_info)
     
     return 0
示例#3
0
文件: tree.py 项目: sshyran/debreate
 def InitMountItems(self):
     # Failsafe conditional in case of errors reading user home directory
     home_exists = os.path.isdir(PATH_home)
     if home_exists:
         home_item = self.AppendItem(self.root_item, GT(u'Home directory'), PATH_home,
                 ImageList.GetImageIndex(u'folder-home'),
                 expImage=ImageList.GetImageIndex(u'folder-home-open'))
         
         self.mount_list.append(home_item)
     
     # List storage devices currently mounted on system
     stdevs = GetMountedStorageDevices()
     
     for DEV in stdevs:
         # Do not re-add home directory in case it is mounted on its own partition
         if DEV.MountPoint == PATH_home:
             continue
         
         add_item = os.path.ismount(DEV.MountPoint)
         
         if add_item:
             for PITEM in self.mount_list:
                 if DEV.MountPoint == PITEM.Path:
                     add_item = False
                     break
         
         if add_item:
             Logger.Debug(__name__, u'Adding new mount PathItem instance: {}'.format(DEV.Label))
             
             self.mount_list.append(self.AppendItem(self.root_item, DEV.Label, DEV.MountPoint,
                     ImageList.GetImageIndex(DEV.Type)))
             continue
         
         else:
             Logger.Debug(__name__, u'PathItem instance for "{}" directory already exists'.format(DEV.MountPoint))
示例#4
0
    def OnTimerStop(self, event=None):
        Logger.Debug(__name__, u'OnTimerStop')

        if not self.timer.IsRunning():
            Logger.Debug(__name__, GT(u'Timer is stopped'))

        else:
            Logger.Debug(__name__, GT(u'Timer is running'))

        if self.build_error:
            error_lines = self.build_error[:-1]
            error_output = self.build_error[-1]

            ShowErrorDialog(error_lines, error_output, self)

            # Needs to be reset or error dialog will successively show
            self.build_error = None

            return

        msg_lines = (
            GT(u'Quick build complete'),
            self.input_target.GetValue(),
        )

        ShowMessageDialog(msg_lines, GT(u'Build Complete'), module=__name__)
示例#5
0
文件: md5.py 项目: sshyran/debreate
def WriteMD5(stage_dir, parent=None):
    CMD_md5sum = GetExecutable(u'md5sum')

    # Show an error if the 'md5sum' command does not exist
    # This is only a failsafe & should never actually occur
    if not CMD_md5sum:
        if not parent:
            parent = GetMainWindow()

        md5_label = GetField(pgid.BUILD, chkid.MD5).GetLabel()

        err_msg1 = GT(u'The "md5sum" command was not found on the system.')
        err_msg2 = GT(u'Uncheck the "{}" box.').format(md5_label)
        err_msg3 = GT(
            u'Please report this error to one of the following addresses:')
        err_url1 = u'https://github.com/AntumDeluge/debreate/issues'
        err_url2 = u'https://sourceforge.net/p/debreate/bugs/'

        Logger.Error(
            __name__,
            u'{} {} {}\n\t{}\n\t{}'.format(err_msg1, err_msg2, err_msg3,
                                           err_url1, err_url2))

        md5_error = ErrorDialog(parent,
                                text=u'{}\n{}\n\n{}'.format(
                                    err_msg1, err_msg2, err_msg3))
        md5_error.AddURL(err_url1)
        md5_error.AddURL(err_url2)
        md5_error.ShowModal()

        return None

    temp_list = []
    md5_list = []  # Final list used to write the md5sum file
    for ROOT, DIRS, FILES in os.walk(stage_dir):
        # Ignore the 'DEBIAN' directory
        if os.path.basename(ROOT) == u'DEBIAN':
            continue

        for F in FILES:
            F = u'{}/{}'.format(ROOT, F)

            md5 = GetCommandOutput(CMD_md5sum, (u'-t', F))

            Logger.Debug(__name__,
                         u'WriteMD5: GetCommandOutput: {}'.format(md5))

            temp_list.append(md5)

    for item in temp_list:
        # Remove [stage_dir] from the path name in the md5sum so that it has a
        # true unix path
        # e.g., instead of "/myfolder_temp/usr/local/bin", "/usr/local/bin"
        sum_split = item.split(u'{}/'.format(stage_dir))
        sum_join = u''.join(sum_split)
        md5_list.append(sum_join)

    # Create the md5sums file in the "DEBIAN" directory
    return WriteFile(u'{}/DEBIAN/md5sums'.format(stage_dir),
                     u'{}\n'.format(u'\n'.join(md5_list)))
示例#6
0
	def Export(self, out_dir, executable=True, build=False):
		if not os.path.isdir(out_dir):
			Logger.Error(__name__, GT(u'Directory not available: {}'.format(out_dir)))

			return (ERR_DIR_NOT_AVAILABLE, __name__)

		if build:
			absolute_filename = ConcatPaths((out_dir, self.FileName))

		else:
			filename = u'{}-{}'.format(page_ids[self.Parent.GetId()].upper(), self.FileName)
			absolute_filename = ConcatPaths((out_dir, filename))

		script_text = u'{}\n\n{}'.format(self.GetShebang(), self.ScriptBody.GetValue())

		WriteFile(absolute_filename, script_text)

		if not os.path.isfile(absolute_filename):
			Logger.Error(__name__, GT(u'Could not write to file: {}'.format(absolute_filename)))

			return (ERR_FILE_WRITE, __name__)

		if executable:
			os.chmod(absolute_filename, 0755)

		return (0, None)
示例#7
0
    def OnMenuChangePage(self, event=None):
        page_id = None

        if event:
            page_id = event.GetId()

            Logger.Debug(__name__,
                         GT(u'Page ID from menu event: {}').format(page_id))

        else:
            for M in self.GetMenu(menuid.PAGE).GetMenuItems():
                if M.IsChecked():
                    page_id = M.GetId()

                    Logger.Debug(
                        __name__,
                        GT(u'Page ID from menu item: {}').format(page_id))

                    break

        if page_id == None:
            Logger.Error(__name__, GT(u'Could not get page ID'))

            return

        self.Wizard.ShowPage(page_id)
示例#8
0
    def ProjectSaveAs(self):
        wildcards = (
            u'{} (.{})'.format(GT(u'Debreate project files'), PROJECT_ext),
            u'*.{}'.format(PROJECT_ext),
        )

        save_dialog = GetFileSaveDialog(self, GT(u'Save Debreate Project'),
                                        wildcards, PROJECT_ext)

        if ShowDialog(save_dialog):
            project_path = save_dialog.GetPath()
            project_filename = save_dialog.GetFilename()

            Logger.Debug(__name__,
                         GT(u'Project save path: {}').format(project_path))
            Logger.Debug(
                __name__,
                GT(u'Project save filename: {}').format(project_filename))

            saved = self.ProjectSave(project_path)
            if saved == dbrerrno.SUCCESS:
                self.ProjectSetDirty(False)

            return saved

        Logger.Debug(__name__, GT(u'Not saving project'))

        return dbrerrno.ECNCLD
示例#9
0
	def DeleteAllItems(self):
		if ListCtrl.DeleteAllItems(self):
			self.FileItems = []
		else:
			Logger.Warn(__name__, u'Failed to delete all items from FileList')

		Logger.Debug(__name__, u'Visual item count: {}'.format(self.GetItemCount()))
		Logger.Debug(__name__, u'Acutal item count: {}'.format(len(self.FileItems)))
示例#10
0
	def ImportExes(self, event=None):
		event_id = event.GetId()
		if event_id == btnid.IMPORT:
			# First clear the Auto-Link display and the executable list
			self.Executables.Reset()

			# Get executables from "files" tab
			file_list = GetField(pgid.FILES, inputid.LIST)

			for INDEX in range(file_list.GetItemCount()):
				# Get the filename from the source
				file_name = file_list.GetFilename(INDEX, basename=True)
				file_path = file_list.GetPath(INDEX)
				# Where the file linked to will be installed
				file_target = file_list.GetItem(INDEX, 1)

				# Walk directory to find executables
				if file_list.IsDirectory(INDEX):
					for EXE in GetFiles(file_path, os.X_OK):
						self.Executables.Append(FileItem(EXE, file_target))

				# Search for executables (distinguished by red text)
				elif file_list.IsExecutable(INDEX):
					try:
						# If destination doesn't start with "/" do not include executable
						if file_target.GetText()[0] == u'/':
							if file_target.GetText()[-1] == u'/' or file_target.GetText()[-1] == u' ':
								# In case the full path of the destination is "/" keep going
								if len(file_target.GetText()) == 1:
									dest_path = u''

								else:
									search = True
									# Set the number of spaces to remove from dest path in case of multiple "/"
									slashes = 1
									while search:
										# Find the number of slashes/spaces at the end of the filename
										endline = slashes - 1
										if file_target.GetText()[-slashes] == u'/' or file_target.GetText()[-slashes] == u' ':
											slashes += 1

										else:
											dest_path = file_target.GetText()[:-endline]
											search = False

							else:
								dest_path = file_target.GetText()

							self.Executables.Append(file_name, dest_path)

						else:
							Logger.Warn(__name__, u'{}: The executables destination is not valid'.format(__name__))

					except IndexError:
						Logger.Warn(__name__, u'{}: The executables destination is not available'.format(__name__))

		elif event_id in (btnid.REMOVE, wx.WXK_DELETE):
			self.Executables.RemoveSelected()
示例#11
0
    def ShowLog(self):
        self.RefreshLog()
        self.Show(True)

        if not self.LogPollThread.IsActive():
            Logger.Debug(__name__, u'Starting log polling thread ...')

            self.LogPollThread.Start()

        else:
            Logger.Debug(__name__, u'Log polling thread is already started')
示例#12
0
	def Delete(self, item):
		item = self.GetIndex(item)
		filename = self.GetPath(item)

		if self.DeleteItem(item):
			self.FileItems.pop(item)

			Logger.Debug(__name__, u'Deleted item from BasicFileList: {}'.format(filename))
			return True

		Logger.Warn(__name__, u'Failed to deleted item from BasicFilelist: {}'.format(filename))
		return False
示例#13
0
    def __init__(self,
                 parent,
                 title=u'Choose a file',
                 defaultDir=None,
                 defaultFile=u'',
                 defaultExt=None,
                 wildcard=wx.FileSelectorDefaultWildcardStr,
                 style=wx.FD_DEFAULT_STYLE,
                 pos=wx.DefaultPosition,
                 size=wx.DefaultSize,
                 name=u'filedlg'):

        if not defaultDir:
            defaultDir = os.getcwd()

        # Setting os.getcwd() causes dialog to always be opened in working directory
        wx.FileDialog.__init__(self, parent, title, defaultDir, defaultFile,
                               wildcard, style, pos)

        self.SetSize(size)
        self.SetName(name)
        self.Extension = defaultExt

        if self.WindowStyleFlag & wx.FD_SAVE:
            wx.EVT_BUTTON(self, self.AffirmativeId, self.OnAccept)

            if self.WindowStyleFlag & wx.FD_CHANGE_DIR:
                Logger.Warn(
                    __name__,
                    u'Found FD_CHANGE_DIR style, could conflict with OnAccept method'
                )

        self.CenterOnParent()
示例#14
0
    def IsOkay(self):
        Logger.Warn(
            __name__,
            GT(u'Page {} does not override inherited method IsOkay').format(
                self.GetName()))

        return False
示例#15
0
def ShowMessageDialog(text,
                      title=GT(u'Message'),
                      details=None,
                      module=None,
                      parent=None,
                      linewrap=0):
    if not parent:
        parent = GetMainWindow()

    logger_text = text
    if isinstance(text, (tuple, list)):
        logger_text = u'; '.join(text)
        text = u'\n'.join(text)

    if details:
        logger_text = u'{}:\n{}'.format(logger_text, details)

    message_dialog = DetailedMessageDialog(parent,
                                           title,
                                           ICON_INFORMATION,
                                           text,
                                           linewrap=linewrap)
    if details:
        message_dialog.SetDetails(details)

    Logger.Debug(module, logger_text)

    message_dialog.ShowModal()
示例#16
0
    def OnProjectNew(self, event=None):
        Logger.Debug(
            __name__,
            GT(u'Project loaded before OnProjectNew: {}').format(
                self.ProjectIsLoaded()))

        return self.ProjectClose()
示例#17
0
    def ShowLogWindow(self, show=True):
        Logger.Debug(__name__, GT(u'Show log window: {}').format(show))

        self.log_window.Show(show)

        if self.menu_debug.IsChecked(menuid.DEBUG) != show:
            self.menu_debug.Check(menuid.DEBUG, show)
示例#18
0
    def ProjectOpenLegacy(self, data, filename):
        Logger.Debug(__name__, GT(u'Legacy project format (text) detected'))

        def ProjectError():
            wx.MessageDialog(self,
                             GT(u'Not a valid Debreate project'),
                             GT(u'Error'),
                             style=wx.OK | wx.ICON_ERROR).ShowModal()

        if data == wx.EmptyString:
            ProjectError()
            return dbrerrno.EBADFT

        lines = data.split(u'\n')
        app = lines[0].split(u'-')[0].split(u'[')[1]

        if app != u'DEBREATE':
            ProjectError()
            return dbrerrno.EBADFT

        # *** Get Control Data *** #
        control_data = data.split(u'<<CTRL>>\n')[1].split(u'\n<</CTRL>>')[0]
        depends_data = self.Wizard.GetPage(pgid.CONTROL).Set(control_data)
        self.Wizard.GetPage(pgid.DEPENDS).Set(depends_data)

        # *** Get Files Data *** #
        files_data = data.split(u'<<FILES>>\n')[1].split(u'\n<</FILES>>')[0]
        self.Wizard.GetPage(pgid.FILES).Set(files_data)

        # *** Get Scripts Data *** #
        scripts_data = data.split(u'<<SCRIPTS>>\n')[1].split(
            u'\n<</SCRIPTS>>')[0]
        self.Wizard.GetPage(pgid.SCRIPTS).Set(scripts_data)

        # *** Get Changelog Data *** #
        clog_data = data.split(u'<<CHANGELOG>>\n')[1].split(
            u'\n<</CHANGELOG>>')[0]
        self.Wizard.GetPage(pgid.CHANGELOG).Set(clog_data)

        # *** Get Copyright Data *** #
        try:
            cpright_data = data.split(u'<<COPYRIGHT>>\n')[1].split(
                u'\n<</COPYRIGHT')[0]
            self.Wizard.GetPage(pgid.COPYRIGHT).Set(cpright_data)

        except IndexError:
            pass

        # *** Get Menu Data *** #
        m_data = data.split(u'<<MENU>>\n')[1].split(u'\n<</MENU>>')[0]
        self.Wizard.GetPage(pgid.LAUNCHERS).Set(m_data)

        # Get Build Data
        build_data = data.split(u'<<BUILD>>\n')[1].split(u'\n<</BUILD')[0]
        self.Wizard.GetPage(pgid.BUILD).Set(build_data)

        self.ProjectSetDirty(False)

        # Legacy projects should return None since we can't save in that format
        return None
示例#19
0
    def __init__(self, cfgKey=None, cfgSect=None):
        self.ConfigKey = cfgKey
        self.ConfigSection = cfgSect

        if not self.ConfigSection:
            self.ConfigSection = u'GENERAL'

        if self.ConfigKey == None:
            self.ConfigKey = self.GetName()

        # Add to recognized configuration keys
        SetDefaultConfigKey(self.ConfigKey, self.GetDefaultValue())

        # Set state using config file if found
        state = ReadConfig(self.ConfigKey)

        ret_codes = (
            ConfCode.FILE_NOT_FOUND,
            ConfCode.KEY_NOT_DEFINED,
            ConfCode.KEY_NO_EXIST,
        )

        # FIXME:
        if state not in (ret_codes):
            self.SetValue(state)

        else:
            Logger.Debug(__name__, u'Key not found: {}'.format(self.ConfigKey))

        # *** Event Handling *** #

        if isinstance(self, wx.CheckBox):
            self.Bind(wx.EVT_CHECKBOX, self.OnToggle)
示例#20
0
    def GetPage(self, pageId):
        for P in self.Pages:
            if P.GetId() == pageId:
                return P

        Logger.Warn(__name__,
                    u'Page with ID {} has not been constructed'.format(pageId))
示例#21
0
def LaunchFirstRun(debreate_app):
    FR_dialog = FirstRun()
    debreate_app.SetTopWindow(FR_dialog)
    FR_dialog.ShowModal()

    init_conf_code = InitializeConfig()

    Logger.Debug(
        __name__, u'Configuration initialized: {}'.format(
            init_conf_code == ConfCode.SUCCESS))

    if (init_conf_code !=
            ConfCode.SUCCESS) or (not os.path.isfile(default_config)):
        msg_l1 = GT(
            u'An error occurred trying to create the configuration file:')
        msg_l2 = GT(u'Please report this error to Debreate\'s developers')
        ShowErrorDialog(u'{} {}\n\n{}'.format(msg_l1, default_config, msg_l2))

        return init_conf_code

    FR_dialog.Destroy()

    # Delete first run dialog from memory
    del (FR_dialog)

    return init_conf_code
示例#22
0
    def InitPage(self):
        Logger.Debug(
            __name__,
            GT(u'Page {} does not override inherited method InitPage').format(
                self.GetName()))

        return False
示例#23
0
    def ExportBuild(self, target):
        Logger.Warn(
            __name__,
            GT(u'Page {} does not override inherited method ExportBuild').
            format(self.GetName()))

        return (dbrerrno.SUCCESS, None)
示例#24
0
    def OnAccept(self, event=None):
        if self.IsSaveDialog():
            if self.Extension:
                if not self.Filename.endswith(self.Extension):
                    # Adds extensions if not specified
                    self.SetFilename(self.Filename)

            if self.Path:
                if os.path.isfile(self.Path):
                    overwrite = OverwriteDialog(self, self.Path)

                    if not ShowDialog(overwrite):
                        return

                    try:
                        os.remove(self.Path)

                    except OSError:
                        # File was removed before confirmation
                        Logger.Debug(
                            __name__,
                            u'Item was removed before confirmation: {}'.format(
                                self.Path))

        # Because we are not using default FileDialog methods, we must set
        # directory manually.
        self.SetDirectory(os.path.dirname(self.Path))

        # File & directory dialogs should call this function
        ChangeWorkingDirectory(self.GetDirectory())

        self.EndModal(self.AffirmativeId)
示例#25
0
    def OnExportLauncher(self, event=None):
        Logger.Debug(__name__, u'Export launcher ...')

        export = GetFileSaveDialog(GetMainWindow(), GT(u'Save Launcher'))

        if ShowDialog(export):
            target = export.GetPath()

            # Create a backup file
            # FIXME: Create backup files in WriteFile function?
            overwrite = False
            if os.path.isfile(target):
                backup = u'{}.backup'.format(target)
                shutil.copy(target, backup)
                overwrite = True

            try:
                self.ExportToFile(target)

                if overwrite:
                    os.remove(backup)

            except UnicodeEncodeError:
                detail1 = GT(
                    u'Unfortunately Debreate does not support unicode yet.')
                detail2 = GT(
                    u'Remove any non-ASCII characters from your project.')

                ShowErrorDialog(GT(u'Save failed'),
                                u'{}\n{}'.format(detail1, detail2),
                                title=GT(u'Unicode Error'))

                os.remove(target)
                # Restore from backup
                shutil.move(backup, target)
示例#26
0
文件: ident.py 项目: sshyran/debreate
 def GetIdLabel(self, pageId):
     if pageId in self.Labels:
         return self.Labels[pageId]
     
     Logger.Warn(__name__, u'Label for ID {} not found'.format(pageId))
     
     return wx.EmptyString
示例#27
0
    def AddFile(self, filename, sourceDir, targetDir=None, executable=False):
        list_index = self.GetItemCount()

        # Method can be called with two argements: absolute filename & target directory
        if targetDir == None:
            targetDir = sourceDir
            sourceDir = os.path.dirname(filename)
            filename = os.path.basename(filename)

        source_path = ConcatPaths((sourceDir, filename))

        Logger.Debug(__name__, GT(u'Adding file: {}').format(source_path))

        self.InsertStringItem(list_index, filename)
        self.SetStringItem(list_index, columns.SOURCE, sourceDir)
        self.SetStringItem(list_index, columns.TARGET, targetDir)
        self.SetStringItem(list_index, columns.TYPE,
                           GetFileMimeType(source_path))

        if os.path.isdir(source_path):
            self.SetItemTextColour(list_index, self.FOLDER_TEXT_COLOR)

        else:
            # TODO: Use 'GetFileMimeType' module to determine file type
            if os.access(source_path, os.X_OK) or executable:
                self.SetFileExecutable(list_index)

            if not os.path.isfile(source_path):
                self.SetItemBackgroundColour(list_index, COLOR_warn)

                # File was added but does not exist on filesystem
                return False

        return True
示例#28
0
def ParseMountedDevices():
	# FIXME: Identify labels for different systems & drive types
	device_labels = (
		u'/dev/sd',
		)

	# Empty the device list
	mounted_devices = {}

	if os.path.isfile(u'/etc/mtab'):
		mtab = ReadFile(u'/etc/mtab', split=True, convert=list)

		# Only keep lines referring to devices directory
		for X in reversed(range(len(mtab))):
			if not mtab[X].startswith(u'/dev'):
				mtab.pop(X)

		mtab.sort()

		for LINE in mtab:
			LINE = LINE.split(u' ')

			device = LINE[0]
			mount_point = LINE[1]

			for LABEL in device_labels:
				if device.startswith(LABEL):
					mounted_devices[device] = mount_point

	else:
		Logger.Warn(__name__, u'/etc/mtab file does not exist. Mounted devices list will be empty')

	return mounted_devices
示例#29
0
    def OnWizardBtnPage(self, event=None):  #@UnusedVariable
        ID = self.Wizard.GetCurrentPageId()
        Logger.Debug(__name__,
                     GT(u'Event: EVT_CHANGE_PAGE, Page ID: {}').format(ID))

        menu_page = self.GetMenu(menuid.PAGE)
        if not menu_page.IsChecked(ID):
            menu_page.Check(ID, True)
示例#30
0
    def InstallPackage(self, package):
        system_installer = GetSystemInstaller()

        if not system_installer:
            ShowErrorDialog(
                GT(u'Cannot install package'),
                GT(u'A compatible package manager could not be found on the system'
                   ),
                __name__,
                warn=True)

            return

        Logger.Info(__name__,
                    GT(u'Attempting to install package: {}').format(package))
        Logger.Info(__name__,
                    GT(u'Installing with {}').format(system_installer))

        install_cmd = (
            system_installer,
            package,
        )

        wx.Yield()
        # FIXME: Use ExecuteCommand here
        install_output = subprocess.Popen(install_cmd)

        # Command appears to not have been executed correctly
        if install_output == None:
            ShowErrorDialog(GT(u'Could not install package: {}'),
                            GT(u'An unknown error occurred'), __name__)

            return

        # Command executed but did not return success code
        if install_output.returncode:
            err_details = (
                GT(u'Process returned code {}').format(
                    install_output.returncode),
                GT(u'Command executed: {}').format(u' '.join(install_cmd)),
            )

            ShowErrorDialog(GT(u'An error occurred during installation'),
                            u'\n'.join(err_details), __name__)

            return