Publisher created inside a class's constructor is not publishing

Hi all, I have created a class to take off, move around and land a drone. The code below worked perfectly when invoked directly.

But when the same class is called from a separate python file using its object, its failed to get past the class’s constructor.

File name: move_drone.py

#! /usr/bin/env python
import rospy
from geometry_msgs.msg import Twist
from std_msgs.msg import Empty


class move_drone_class:
    def __init__(self):
        self.tw=Twist()

        #created publisher to move the robot using topic /cmd_vel
        self.pub_cmd_vel=rospy.Publisher("/cmd_vel",Twist,queue_size=1)        
        while self.pub_cmd_vel.get_num_connections() < 1:
            print("Waiting on /cmd_vel publisher")
            #or use "Pass" here

        #create publisher to takeoff using topic /drone/takeoff
        self.pub_takeoff=rospy.Publisher("/drone/takeoff",Empty,queue_size=1)
        while self.pub_takeoff.get_num_connections() < 1:
            print("Waiting on /takeoff publisher")
            #or use "Pass" here

        #create publisher to land using topic /drone/land
        self.pub_land=rospy.Publisher("/drone/land",Empty,queue_size=1)
        while self.pub_land.get_num_connections() < 1:
            print("Waiting on /land publisher")
            #or use "Pass" here
        

    def takeoff(self):
        self.pub_takeoff.publish(Empty())
        print("Taking Off-----------------")
    

    def land(self):
        self.pub_land.publish(Empty())
        print("Landing---------------------")
    
    def move_around_in_random(self):
        print("Moving Around")
        self.tw.linear.x=0.8
        self.tw.angular.z=0.8
        self.pub_cmd_vel.publish(self.tw)


if __name__ == '__main__':
    rospy.init_node("drone_node")
    drone_obj=move_drone_class()
    #the code below takeoff the drone, moves it around for 3 seconds and then lands it
    try:
        drone_obj.takeoff()
        drone_obj.move_around_in_random()
        rospy.sleep(3)
        drone_obj.land()
    except rospy.ROSInterruptException:
        pass
    

Below snippet is the constructor that the accessing program drone_client.py is not able to get past

#created publisher to move the robot using topic /cmd_vel
        self.pub_cmd_vel=rospy.Publisher("/cmd_vel",Twist,queue_size=1)        
        while self.pub_cmd_vel.get_num_connections() < 1:
            print("Waiting on /cmd_vel publisher")
            #or use "Pass" here

        #create publisher to takeoff using topic /drone/takeoff
        self.pub_takeoff=rospy.Publisher("/drone/takeoff",Empty,queue_size=1)
        while self.pub_takeoff.get_num_connections() < 1:
            print("Waiting on /takeoff publisher")
            #or use "Pass" here

        #create publisher to land using topic /drone/land
        self.pub_land=rospy.Publisher("/drone/land",Empty,queue_size=1)
        while self.pub_land.get_num_connections() < 1:
            print("Waiting on /land publisher")
            #or use "Pass" here

The code for drone_client.py, which is the file accessing the class is as below.
#! /usr/bin/env python

import rospy

import time

import actionlib

from ardrone_as.msg import ArdroneAction, ArdroneGoal, ArdroneResult, ArdroneFeedback

from move_drone import move_drone_class

move_drone_obj=move_drone_class()

nImage = 1

# definition of the feedback callback. This will be called when feedback

# is received from the action server

# it just prints a message indicating a new message has been received

def feedback_callback(feedback):

    global nImage

    print('[Feedback] image n.%d received'%nImage)

    nImage += 1

# initializes the action client node

rospy.init_node('drone_action_client')

print("node initialised")

# create the connection to the action server

client = actionlib.SimpleActionClient('/ardrone_action_server', ArdroneAction)

# waits until the action server is up and running

print("waiting for server connection")

client.wait_for_server()

print("server connected")

# creates a goal to send to the action server

goal = ArdroneGoal()

goal.nseconds = 10 # indicates, take pictures along 10 seconds

# sends the goal to the action server, specifying which feedback function

# to call when feedback received

client.send_goal(goal, feedback_cb=feedback_callback)

# Uncomment these lines to test goal preemption:

#time.sleep(3.0)

#client.cancel_goal()  # would cancel the goal 3 seconds after starting

# wait until the result is obtained

# you can do other stuff here instead of waiting

# and check for status from time to time 

# status = client.get_state()

# check the client API link below for more info

# client.wait_for_result()

move_drone_obj.takeoff()

while client.get_state()<2:

    move_drone_obj.move_around_in_random()

    # print(client.ger)

move_drone_obj.land()

print('[Result] State: %d'%(client.get_state()))

The Action server used for the drone was

roslaunch ardrone_as action_server.launch

Let me know if you need more info, the only other topic where i found something similar was
click link

Guys, I solved it. what happened was that the class file also needs to have a node initialization by adding rospy.init_node("drone_action_client").The node name mentioned in the class and the node name in the calling program should be the same. The snippet of change is as follows:

def __init__(self):
        self.tw=Twist()
        
        rospy.init_node("drone_action_client")
        #created publisher to move the robot using topic /cmd_vel
        self.pub_cmd_vel=rospy.Publisher("/cmd_vel",Twist,queue_size=1)        
        while self.pub_cmd_vel.get_num_connections() < 1:
            print("Waiting on /cmd_vel publisher")
            #or use "Pass" here

Alternatively, create the class’s object only after the node is initialized in the calling file. This should have been the go to method rather than the mess above. The working code should look like this:
drone_client.py

#! /usr/bin/env python
import rospy
import time
import actionlib
from ardrone_as.msg import ArdroneAction, ArdroneGoal, ArdroneResult, ArdroneFeedback
from move_drone import move_drone_class


nImage = 1
# definition of the feedback callback. This will be called when feedback
# is received from the action server
# it just prints a message indicating a new message has been received
def feedback_callback(feedback):
    global nImage
    print('[Feedback] image n.%d received'%nImage)
    nImage += 1


# initializes the action client node
rospy.init_node('drone_action_client')
print("node initialised")
# move_drone_obj=move_drone_class('drone_action_client')
move_drone_obj=move_drone_class()
# create the connection to the action server
client = actionlib.SimpleActionClient('/ardrone_action_server', ArdroneAction)
# waits until the action server is up and running
print("waiting for server connection")
client.wait_for_server()
print("server connected")
# creates a goal to send to the action server
goal = ArdroneGoal()
goal.nseconds = 10 # indicates, take pictures along 10 seconds

# sends the goal to the action server, specifying which feedback function
# to call when feedback received
client.send_goal(goal, feedback_cb=feedback_callback)

# Uncomment these lines to test goal preemption:
#time.sleep(3.0)
#client.cancel_goal()  # would cancel the goal 3 seconds after starting

# wait until the result is obtained
# you can do other stuff here instead of waiting
# and check for status from time to time 
# status = client.get_state()
# check the client API link below for more info

# client.wait_for_result()
move_drone_obj.takeoff()
while client.get_state()<2:
    move_drone_obj.move_around_in_random()
    # print(client.ger)
move_drone_obj.land()

print('[Result] State: %d'%(client.get_state()))

and the class for moving the drone around is as below:
move_drone.py

#! /usr/bin/env python
import rospy
from geometry_msgs.msg import Twist
from std_msgs.msg import Empty


class move_drone_class:
    def __init__(self):
        self.tw=Twist()
        
        self.pub_cmd_vel=rospy.Publisher("/cmd_vel",Twist,queue_size=1)        
        while self.pub_cmd_vel.get_num_connections() < 1:
            print("Waiting on /cmd_vel publisher")
            #or use "Pass" here

        #create publisher to takeoff using topic /drone/takeoff
        self.pub_takeoff=rospy.Publisher("/drone/takeoff",Empty,queue_size=1)
        while self.pub_takeoff.get_num_connections() < 1:
            print("Waiting on /takeoff publisher")
            #or use "Pass" here

        #create publisher to land using topic /drone/land
        self.pub_land=rospy.Publisher("/drone/land",Empty,queue_size=1)
        while self.pub_land.get_num_connections() < 1:
            print("Waiting on /land publisher")
            #or use "Pass" here
        

    def takeoff(self):
        self.pub_takeoff.publish(Empty())
        print("Taking Off-----------------")
    

    def land(self):
        self.pub_land.publish(Empty())
        print("Landing---------------------")
    
    def move_around_in_random(self):
        # print("Moving Around")
        self.tw.linear.x=0.8
        self.tw.angular.z=0.8
        self.pub_cmd_vel.publish(self.tw)


# if __name__ == '__main__':
#     rospy.init_node("drone_node")
#     drone_obj=move_drone_class()
#     #the code below takeoff the drone, moves it around for 3 seconds and then lands it
#     try:
#         drone_obj.takeoff()
#         drone_obj.move_around_in_random()
#         rospy.sleep(3)
#         drone_obj.land()
#     except rospy.ROSInterruptException:
#         pass