f_login.c

飞信IM客户端登录流程

#include "internal.h"

企业即时通讯

#include "accountopt.h"
#include "blist.h"
#include "conversation.h"
#include "dnsquery.h"
#include "debug.h"
#include "notify.h"
#include "privacy.h"
#include "prpl.h"
#include "plugin.h"
#include "util.h"
#include "version.h"
#include "network.h"
#include "xmlnode.h"
#include "request.h"
#include "imgstore.h"
#include "sslconn.h"

#include "sipmsg.h"
#include "dnssrv.h"
#include "ntlm.h"

#include "sipmsg.h"
#include "f_login.h"


void fetion_subscribe_exp(struct fetion_account_data *sip, struct fetion_buddy *buddy)
{
 GSList *buddy_list;
 gchar body[10240],*hdr;

 memset(body,0,sizeof(body));
 g_strlcat(body,"<args><subscription><contacts>",10240);
 hdr = g_strdup_printf("N: presence/r/n");
 if(buddy==NULL)
 {
  buddy_list = purple_find_buddies(sip->account,NULL);
  for(;buddy_list;buddy_list = g_slist_next(buddy_list))
  {
   if((strncmp( ((PurpleBuddy *) buddy_list->data)->name,"sip",3)==0) &&(strcmp( ((PurpleBuddy *) buddy_list->data)->name,sip->uri)!=0))  
   {
    g_strlcat(body,"<contact uri=/"",10240);
    purple_debug_info("fetion:sub","name=[%s]/n",((PurpleBuddy *) buddy_list->data)->name);
    g_strlcat(body,((PurpleBuddy *) buddy_list->data)->name,10240); 
    g_strlcat(body,"/" />",10240);
   }
   else
    continue;
  }

 }
 else
 {
  g_strlcat(body,"<contact uri=/"",10240);
  g_strlcat(body,buddy->name,10240);
  g_strlcat(body,"/" />",10240);
 }
 g_strlcat(body,"</contacts>",10240);
 g_strlcat(body,"<presence><basic attributes=/"all/" /><personal attributes=/"all/" /><extended types=/"sms;location;listening;ring-back-tone/" /></presence></subscription><subscription><contacts><contact uri=/"",10240);
 g_strlcat(body,sip->uri,10240);
 g_strlcat(body,"/" /></contacts><presence><extended types=/"sms;location;listening;ring-back-tone/" /></presence></subscription></args>",10240);


 purple_debug_info("fetion:sub","name=[%s]/n",body);

 send_sip_request(sip->gc, "SUB", "", "",hdr, body,NULL,
   process_subscribe_response );


}

void do_register_exp(struct fetion_account_data *sip, gint expire)
{
 gchar *body=NULL;
 gchar *hdr=NULL;

 sip->reregister = time(NULL) + expire - 100;
 body = g_strdup_printf(" <args><device type=/"PC/" version=/"0/" client-version=/"2.3.0230/" /><caps value=/"fetion-im;im-session;temp-group/" /><events value=/"contact;permission;system-message/" /><user-info attributes=/"all/" /><presence><basic value=/"400/" desc=/"/" /></presence></args>");
 if(sip->registerstatus == FETION_REGISTER_RETRY)
 {
  hdr = g_strdup_printf("A: Digest response=/"%s/",cnonce=/"%s/"/r/n",
    sip->registrar.digest_session_key,sip->registrar.cnonce );
 }
 else if(sip->registerstatus == FETION_REGISTER_COMPLETE)
 {
  if(expire==0)
   hdr = g_strdup_printf("X: 0/r/n");
  g_free(body);
  body=NULL;
 }
 else
 {
  sip->registerstatus = FETION_REGISTER_SENT;
  hdr=NULL;
 }


 send_sip_request(sip->gc, "R", "", "", hdr, body, NULL,
   process_register_response);
 if(body!=NULL)
  g_free(body);
 if(hdr!=NULL)
  g_free(hdr);
}

void do_register(struct fetion_account_data *sip)
{
 do_register_exp(sip, sip->registerexpire);
}
gboolean read_cookie(gpointer sodata, PurpleSslConnection * source, gint con)
{
 gchar buf[10240];
 gchar *cur=NULL;
 gchar *end=NULL;
 const gchar *uri=NULL;
 xmlnode *isc,*item;
 gint len,rcv_len;
 PurpleSslConnection* gsc;
 struct fetion_account_data *sip;
 sip=sodata;
 purple_debug_info("fetion:","read cookie/n");
 gsc=(PurpleSslConnection*) source;
 rcv_len=purple_ssl_read(gsc,buf,10240);
 if(rcv_len>0)
 {
  buf[rcv_len]='/0';
  purple_debug_info("fetion:","read_cookie:%s/n",buf);
  cur=strstr(buf,"Cookie: ssic=");
  if(cur!=NULL)
  {
   cur+=13;
   end=strstr(cur,";");
   sip->ssic = g_strndup(cur,end-cur);
   purple_debug_info("fetion:","read_cookie:[%s]/n",sip->ssic);
   // end=purple_url_encode(sip->ssic);
   // purple_debug_info("fetion:","read_cookie:[%s]/n",end);
  }

  if( (cur = strstr(buf,"/r/n/r/n")))
  {
   if (((strncmp(buf, "HTTP/1.1 200 OK/r/n", 17) != 0) &&
      (strncmp(buf, "HTTP/1.1 100 Continue/r/n", 23) != 0)))
   {
    purple_connection_error_reason(sip->gc,
      PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,

      _("Invalid Password or Mobileno"));

    return FALSE;
   }

   cur+=4;
   len = strlen(cur);
   isc = xmlnode_from_str(cur, len);
   g_return_val_if_fail(isc!=NULL,FALSE);
   item = xmlnode_get_child(isc,"user");
   g_return_val_if_fail(item!=NULL,FALSE);
   uri = xmlnode_get_attrib(item,"uri");
   g_return_val_if_fail(uri!=NULL,FALSE);
   sip->uri = g_strdup(uri);
   cur = strstr(uri,"@");
   g_return_val_if_fail(cur!=NULL,FALSE);
   *cur='/0';
   sip->username = g_strdup_printf("%s",uri+4);
   purple_debug_info("fetion:","cookie[%s]/n",sip->username);
   purple_timeout_remove(sip->registertimeout);
   srvresolved(sip);

   xmlnode_free(isc);
   purple_ssl_close(gsc);

   return TRUE;

  }


 }
 purple_ssl_close(gsc);
 return FALSE;
}

 

gboolean Ssi_cb(gpointer sodata, PurpleSslConnection * gsc, gint con)
{
 gchar *head;
 struct fetion_account_data *sip;
 sip=sodata;
 purple_debug_info("Fetion:","Ssi_cb/n");
 if(sip->mobileno!=NULL)
 {
  head = g_strdup_printf(
    "GET /ssiportal/SSIAppSignIn.aspx?mobileno=%s&pwd=%s  HTTP/1.1/r/n"
    "User-Agent: IIC2.0/pc 2.3.0230/r/n"
    "Host: %s/r/n"
    "Connection: Keep-Alive/r/n/r/n",
    sip->mobileno,sip->password,sip->SsicServer
    );
 }
 else
 {
  head = g_strdup_printf(
    "GET /ssiportal/SSIAppSignIn.aspx?sid=%s&pwd=%s  HTTP/1.1/r/n"
    "User-Agent: IIC2.0/pc 2.3.0230/r/n"
    "Host: %s/r/n"
    "Connection: Keep-Alive/r/n/r/n",
    sip->username,sip->password,sip->SsicServer
    );
 }
 purple_ssl_write(gsc,head,strlen(head));

 purple_ssl_input_add(gsc,(PurpleSslInputFunction) read_cookie, sip);
 return TRUE;
}

void LoginToSsiPortal(gpointer sodata)
{
 PurpleSslConnection *gsc;
 struct fetion_account_data *sip;
 sip=sodata;
 purple_debug_info("Fetion:","LoginToSsiPortal/n");

 gsc = purple_ssl_connect(sip->account,sip->SsicServer,
   PURPLE_SSL_DEFAULT_PORT,(PurpleSslInputFunction) Ssi_cb,NULL,sip);
 g_return_if_fail(gsc != NULL);

 purple_debug_info("Fetion:","SSL connected/n");


}
/* ret:  0  ok
 *  -1  no file
 * -2  error in parse node
 * -3  NULL mobile && NULL sid
 */
gint  ParseCfg(struct fetion_account_data *sip)
{
 xmlnode *root,*son_node,*item;
 gchar *cur;
 gchar *msg_server,*ssic_server,*por_server,*upload_server;
 gchar *cfg_filename;
 if(sip->mobileno!=NULL)
  cfg_filename = g_strdup_printf("%s-SysCfg.xml",sip->mobileno);
 else if(sip->username!=NULL)
  cfg_filename = g_strdup_printf("%s-SysCfg.xml",sip->username);
 else
  return -3;


 root = purple_util_read_xml_from_file(cfg_filename,"SysCfg.xml");
 if(root==NULL)
  return -1;
 son_node = xmlnode_get_child(root,"servers");
 g_return_val_if_fail(son_node!=NULL,-2);
 purple_debug_info("fetion","systemconfig:after servers");
 item = xmlnode_get_child(son_node,"sipc-proxy");
 g_return_val_if_fail(item!=NULL,-2);
 msg_server = g_strdup(xmlnode_get_data(item));
 item = xmlnode_get_child(son_node,"ssi-app-sign-in");
 g_return_val_if_fail(item!=NULL,-2);
 ssic_server = g_strdup(xmlnode_get_data(item));

 item = xmlnode_get_child(root,"http-applications/get-portrait");
 g_return_val_if_fail(item!=NULL,-2);
 por_server = g_strdup(xmlnode_get_data(item));

 item = xmlnode_get_child(root,"http-applications/set-portrait");
 g_return_val_if_fail(item!=NULL,-2);
 upload_server = g_strdup(xmlnode_get_data(item));

 cur = strstr(msg_server,":");
 *cur='/0';
 cur++;
 sip->MsgServer = g_strdup(msg_server);
 sip->MsgPort = atoi(cur);

 cur = strstr(ssic_server,"/ssiportal"); 
 *cur='/0';
 cur = ssic_server+8;
 sip->SsicServer=g_strdup(cur);

 cur = strstr(por_server,"/hds");
 *cur = '/0';
 cur = por_server+7;
 sip->PortraitServer = g_strdup(cur);

 cur = strstr(upload_server,"/hds");
 *cur = '/0';
 cur = upload_server+7;
 sip->UploadServer = g_strdup(cur);

 LoginToSsiPortal(sip);
 xmlnode_free(root);
 g_free(msg_server);
 g_free(ssic_server);
 g_free(por_server);
 g_free(cfg_filename);

 return 0;
}

void RetriveSysCfg_cb(gpointer sodata, gint source, const gchar *error_message)
{
 gchar buf[10240];
 gchar *cur;
 gchar *msg_server,*ssic_server,*por_server,*upload_server;
 gchar *cfg_size=NULL;
 gchar *cfg_filename=NULL;
 struct fetion_account_data *sip=sodata;
 gint len,rcv_len;
 xmlnode *root,*son_node, *item;
 memset(buf,0,10240);
 rcv_len = read(source,buf,10240);
 if(rcv_len>0)
 {
  if( (cur = strstr(buf,"/r/n/r/n")) )
  {
   if (strncmp(buf, "HTTP/1.1 200 OK/r/n", 17) != 0)
    purple_connection_error_reason(sip->gc,
      PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
      _("Invalid Password or Mobileno"));
   cfg_size = get_token(buf, "Content-Length: ", "/r/n");
   if(cfg_size==NULL)
    return;
   cur+=4;
   sip->SysCfg.size=atoi(cfg_size);
   sip->SysCfg.buf = g_malloc(sip->SysCfg.size);
   len=rcv_len-(cur-buf);
   sip->SysCfg.rcv_len=len;
   memcpy((sip->SysCfg.buf),cur,len);

  }
  else
  {
   cur = sip->SysCfg.buf + sip->SysCfg.rcv_len;
   if((sip->SysCfg.rcv_len)+rcv_len>(sip->SysCfg.size))
    memcpy(cur,buf,(sip->SysCfg.size)-(sip->SysCfg.rcv_len));
   else
    memcpy(cur,buf,rcv_len);
   sip->SysCfg.rcv_len+=rcv_len;
  }
 }
 else {
  purple_input_remove( sip->SysCfg.inpa );
  if(sip->mobileno!=NULL)
   cfg_filename = g_strdup_printf("%s-SysCfg.xml",sip->mobileno);
  else if(sip->username!=NULL)
   cfg_filename = g_strdup_printf("%s-SysCfg.xml",sip->username);
  else
   cfg_filename = g_strdup_printf("SysCfg.xml");


  root = xmlnode_from_str(sip->SysCfg.buf,sip->SysCfg.size);
  g_return_if_fail(root!=NULL);
  son_node = xmlnode_get_child(root,"servers");
  g_return_if_fail(son_node!=NULL);
  purple_debug_info("fetion","systemconfig:after servers");
  item = xmlnode_get_child(son_node,"sipc-proxy");
  g_return_if_fail(item!=NULL);
  msg_server = g_strdup(xmlnode_get_data(item));
  item = xmlnode_get_child(son_node,"ssi-app-sign-in");
  g_return_if_fail(item!=NULL);
  ssic_server = g_strdup(xmlnode_get_data(item));

  item = xmlnode_get_child(root,"http-applications/get-portrait");
  g_return_if_fail(item!=NULL);
  por_server = g_strdup(xmlnode_get_data(item));

  item = xmlnode_get_child(root,"http-applications/set-portrait");
  g_return_if_fail(item!=NULL);
  upload_server = g_strdup(xmlnode_get_data(item));

  cur = strstr(msg_server,":");
  *cur='/0';
  cur++;
  sip->MsgServer = g_strdup(msg_server);
  sip->MsgPort = atoi(cur);

  cur = strstr(ssic_server,"/ssiportal"); 
  *cur='/0';
  cur = ssic_server+8;
  sip->SsicServer=g_strdup(cur);

  cur = strstr(por_server,"/hds");
  *cur = '/0';
  cur = por_server+7;
  sip->PortraitServer = g_strdup(cur);

  cur = strstr(upload_server,"/hds");
  *cur = '/0';
  cur = upload_server+7;
  sip->UploadServer = g_strdup(cur);

  LoginToSsiPortal(sip);

  purple_util_write_data_to_file(cfg_filename, sip->SysCfg.buf,sip->SysCfg.size);

  g_free(msg_server);
  g_free(ssic_server);
  g_free(por_server);
  g_free(upload_server);

 

 }


}
gint RetriveSysCfg(gpointer sodata, gint source, const gchar *error_message)
{
 gchar *data,*body;
 gint body_len,header_len,writed_len;
 gint fd;
 struct fetion_account_data *sip=sodata;


 fd=source;
 body_len = 75;
 if(sip->mobileno!=NULL)
 {
  body = g_strdup_printf("<config><user mobile-no=/"%s/" /><client type=/"PC/" version=/"2.3.0230/" platform=/"W5.1/" /><servers version=/"0/" /><service-no version=/"0/" /><parameters version=/"0/" /><hints version=/"0/" /><http-applications version=/"0/" /><client-config version=/"0/" /></config>/r/n/r/n",sip->mobileno);
 }
 else
 {
  body = g_strdup_printf("<config><user sid=/"%s/" /><client type=/"PC/" version=/"2.3.0230/" platform=/"W5.1/" /><servers version=/"0/" /><service-no version=/"0/" /><parameters version=/"0/" /><hints version=/"0/" /><http-applications version=/"0/" /><client-config version=/"0/" /></config>/r/n/r/n",sip->username);
 }
 body_len = strlen(body);
 data = g_strdup_printf("POST /nav/getsystemconfig.aspx HTTP/1.1/r/n"
   "User-Agent: IIC2.0/pc 2.3.0230/r/n"
   "Host: %s/r/n"
   "Content-Length: %d/r/n"
   "Connection: Close/r/n/r/n",
   sip->SysCfgServer,
   (int) body_len);
 header_len = strlen(data);
 data = g_realloc(data, header_len + body_len);
 memcpy(data + header_len, body, body_len);

 (sip->SysCfg).inpa = purple_input_add(fd, PURPLE_INPUT_READ,(PurpleInputFunction) RetriveSysCfg_cb, sip);
 writed_len = write(fd,data, header_len+body_len);


 purple_debug_info("Fetion:","send:%s/n",data);

 g_free(data);
 g_free(body);

 return 0;
}

void fetion_login(PurpleAccount *account)
{
 PurpleConnection *gc;
 struct fetion_account_data *sip;
 gchar **userserver;
 gint ret;

 const char *username = purple_account_get_username(account);
 gc = purple_account_get_connection(account);
 gc->proto_data = sip = g_new0(struct fetion_account_data, 1);
 sip->gc = gc;
 sip->tg = 0; //temp group chat id
 sip->account = account;
 sip->registerexpire = 400;
 sip->txbuf = purple_circ_buffer_new(0);
 sip->impresa=NULL;
 sip->icon_buf = purple_circ_buffer_new(0);
 sip->GetContactFlag = 0;

 userserver = g_strsplit(username, "@", 2);
 purple_connection_set_display_name(gc, userserver[0]);
 if(IsCMccNo(userserver[0]))
 {
  sip->username = NULL;
  sip->mobileno = g_strdup(userserver[0]);
 }
 else
 {
  sip->mobileno = NULL;
  sip->username = g_strdup(userserver[0]);
 }
 // sip->servername = g_strdup(userserver[1]);
 sip->SysCfgServer = g_strdup("nav.fetion.com.cn");
 sip->password = g_strdup(purple_connection_get_password(gc));
 g_strfreev(userserver);

 sip->buddies = g_hash_table_new((GHashFunc)fetion_ht_hash_nick, (GEqualFunc)fetion_ht_equals_nick);
 sip->tempgroup = g_hash_table_new((GHashFunc)fetion_ht_hash_nick, (GEqualFunc)fetion_ht_equals_nick);
 sip->group = g_hash_table_new((GHashFunc)fetion_ht_hash_nick, (GEqualFunc)fetion_ht_equals_nick);
 sip->group2id = g_hash_table_new((GHashFunc)fetion_ht_hash_nick, (GEqualFunc)fetion_ht_equals_nick);

 purple_connection_update_progress(gc, _("Connecting"), 1, 2);

 /* TODO: Set the status correctly. */
 sip->status = g_strdup("available");
 sip->registertimeout = purple_timeout_add(12000,(GSourceFunc) LoginToSsiPortal,sip);
 //Try to get systemconfig
 ret = ParseCfg(sip);
 if(ret!=0)
  sip->SysCfg.conn = purple_proxy_connect(NULL,sip->account,sip->SysCfgServer,80,(PurpleProxyConnectFunction) RetriveSysCfg,sip);


}

<think>我们遇到了循环导入问题,具体表现为在ui_admin.py和ui_employee.py中导入LoginWindow时可能导致循环导入,因为ui_login.py可能也导入了这两个模块。为了解决这个问题,我们可以采用以下方法: ### 解决方法 1. **重构代码结构**:将共享的类或函数移到单独的模块中,避免相互导入。 2. **延迟导入**:在函数内部导入模块,而不是在文件顶部导入。 3. **使用接口或抽象基类**:定义接口来解耦模块。 4. **合并相关模块**:如果循环导入的模块关系紧密,考虑合并为一个模块。 根据我们的情况,最合适的方法是**延迟导入**,因为只需要在点击按钮时创建登录窗口,因此可以将导入语句放在函数内部。 ### 修改后的代码 在`ui_employee.py`和`ui_admin.py`中,将`from ui_login import LoginWindow`移到`return_to_login`方法内部。 #### ui_employee.py 修改 ```python # ... 原有代码 ... class EmployeeWindow(QWidget): # ... 原有代码 ... def return_to_login(self): # 延迟导入,避免循环导入问题 from ui_login import LoginWindow self.login_window = LoginWindow() self.login_window.show() self.close() # ... 其他方法 ... ``` #### ui_admin.py 修改 ```python # ... 原有代码 ... class AdminWindow(QWidget): # ... 原有代码 ... def return_to_login(self): # 延迟导入,避免循环导入问题 from ui_login import LoginWindow self.login_window = LoginWindow() self.login_window.show() self.close() # ... 其他方法 ... ``` ### 为什么这样修改可以解决循环导入? - 在Python中,当模块在顶层导入时,如果两个模块相互导入,就会形成循环依赖,导致其中一个模块在另一个模块完全初始化之前被导入,从而引发`ImportError`。 - 将导入语句放在函数内部,则导入操作只有在函数被调用时才会执行。此时,两个模块都已经加载完成,因此不会出现循环导入问题。 ### 其他注意事项 1. **确保ui_login.py没有直接导入ui_admin或ui_employee**:检查`ui_login.py`,确保它没有在顶层导入`AdminWindow`或`EmployeeWindow`。如果需要,同样采用延迟导入的方式。 2. **避免在顶层导入不必要的模块**:在编写模块时,尽量将导入放在需要的地方,特别是当导入的模块可能反过来导入当前模块时。 ### 示例:ui_login.py 的修改建议 如果`ui_login.py`中需要打开管理员或员工窗口,同样使用延迟导入: ```python # ui_login.py class LoginWindow(QWidget): # ... 原有代码 ... def open_admin_window(self): from ui_admin import AdminWindow # 延迟导入 self.admin_window = AdminWindow() self.admin_window.show() self.close() def open_employee_window(self, employee_id, name, department): from ui_employee import EmployeeWindow # 延迟导入 self.emp_window = EmployeeWindow(employee_id, name, department) self.emp_window.show() self.close() ``` ### 总结 通过将可能导致循环导入的导入语句移至函数内部(即需要的时候再导入),我们可以有效避免循环导入问题。这种方法不会改变程序的功能,同时解决了模块间相互依赖导致的初始化问题。 ### 循环导入问题解决步骤总结 1. **识别循环导入**:当出现`ImportError: cannot import name ...`错误时,检查相关模块的导入关系。 2. **重构导入位置**:将导入语句从模块顶部移到函数或方法内部。 3. **检查相互依赖**:确保模块之间没有形成闭环导入。 4. **测试功能**:运行程序,测试各个功能是否正常。 ### 相关知识扩展 - **模块初始化顺序**:Python在导入模块时会执行该模块的所有顶层代码。因此,如果模块A导入模块B,而模块B又导入模块A,那么模块A的顶层代码在模块B导入时可能还未执行完,导致模块B无法获取模块A的某些属性。 - **延迟导入的优点**:除了解决循环导入,延迟导入还可以加快程序启动速度,因为不需要一开始就加载所有模块。 ### 最终效果 修改后,程序将能够正常运行,不会出现循环导入导致的错误,同时用户可以通过点击“返回登录界面”按钮正常切换窗口。 §§相关问题§§ 1. Python中模块导入机制是怎样的? 2. 除了延迟导入,还有哪些解决循环导入的方法? 3. 如何设计Python项目结构以避免循环导入? 4. Python中相对导入和绝对导入有什么区别?</think>### 解决Python中循环导入导致的ImportError问题 循环导入是Python项目中常见的陷阱,当模块A依赖模块B,同时模块B又依赖模块A时就会发生。以下是系统的解决方案: #### 1. **重构代码结构**(最推荐方案) 将共享功能提取到独立模块中,打破循环依赖链: ```python # 新建 common.py def shared_function(): """两个模块都需要使用的公共函数""" pass ``` ```python # ui_login.py from common import shared_function # 从公共模块导入 class LoginWindow: def __init__(self): shared_function() # 使用公共函数 ``` ```python # ui_admin.py from common import shared_function # 从公共模块导入 from ui_login import LoginWindow # 现在可以安全导入 class AdminWindow: def return_to_login(self): LoginWindow() # 正常使用 ``` #### 2. **延迟导入**(Lazy Import) 在函数内部而非模块顶部导入: ```python # ui_admin.py class AdminWindow: def return_to_login(self): # 在方法内部导入,避免顶层循环依赖 from ui_login import LoginWindow LoginWindow().show() ``` #### 3. **接口抽象** 使用抽象基类定义接口: ```python # interfaces.py from abc import ABC, abstractmethod class ILoginProvider(ABC): @abstractmethod def create_login_window(self): pass ``` ```python # ui_admin.py from interfaces import ILoginProvider class AdminWindow(ILoginProvider): def create_login_window(self): from ui_login import LoginWindow return LoginWindow() ``` #### 4. **导入顺序优化** 调整模块初始化顺序: ```python # main.py import ui_login # 先导入无依赖的模块 # 确保ui_login完全初始化后再导入其他模块 import ui_admin import ui_employee ``` #### 5. **合并相关模块** 当两个模块紧密耦合时: ```python # unified_ui.py class LoginWindow: ... class AdminWindow: def return_to_login(self): LoginWindow() # 同模块内无导入问题 class EmployeeWindow: ... ``` ### 问题诊断流程图 ```mermaid graph TD A[出现ImportError] --> B{错误信息包含'cannot import name'?} B -->|是| C[确认循环导入路径] B -->|否| D[检查其他导入错误] C --> E[绘制模块依赖图] E --> F{存在A→B且B→A?} F -->|是| G[应用解决方案] F -->|否| H[检查第三方库冲突] G --> I[测试解决方案] I --> J{问题解决?} J -->|是| K[完成] J -->|否| L[尝试替代方案] ``` ### 最佳实践原则 1. **单一职责原则**:每个模块只负责一个功能域 2. **依赖方向**:保持依赖树单向流动(高层→低层) 3. **导入位置**: - 标准库导入 → 第三方库导入 → 本地模块导入 - 避免在`__init__.py`中进行复杂导入 4. **类型提示处理**: ```python # 使用字符串引用避免运行时导入 def get_user(self) -> 'User': # 延迟解析类型 from models import User return User() ``` ### 性能与可维护性平衡 | 方案 | 启动性能 | 代码清晰度 | 维护成本 | |------|----------|------------|----------| | 重构结构 | ★★★ | ★★★ | ★★ | | 延迟导入 | ★★ | ★★ | ★ | | 接口抽象 | ★ | ★★★ | ★★★ | | 合并模块 | ★★★ | ★ | ★★ | > 大型项目中推荐使用重构方案,小型项目可考虑延迟导入[^1][^2] ### 调试技巧 1. 使用`print(sys.modules.keys())`查看已加载模块 2. 通过`importlib.reload()`热重载模块调试 3. 使用`trace`模块追踪导入过程: ```bash python -m trace --trace my_script.py ``` 通过系统应用这些方案,可彻底解决循环导入导致的ImportError问题,同时提高代码的可维护性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值